Sort

Sort

  • Time Complexity
  • Space Complexity
  • Stable, unstable

https://www.hackerearth.com/practice/algorithms/sorting/merge-sort/visualize/

Quick Sort

 private static void quickSort(int[] arr, int left, int right) {
        if (left + 10 <= right) {
            int pivot = medianOfThree(arr, left, right);
            int low = left, high = right - 1;
            while (true) {
                while (arr[++low] < pivot);
                while (arr[--high] > pivot);
                if (low < high) swap(arr, low, high);
                else break;
            }
            swap(arr, low, right - 1);
            quickSort(arr, left, low - 1);
            quickSort(arr, low + 1, right);
        } else {
            insertionSort(arr, left, right);
        }
    }

    public static void quicksort(int[] arr) {
        quickSort(arr, 0, arr.length - 1);
    }

Quick Sort

def quick_sort(arr, left, right):
    if left + 10 <= right:
        pivot = median_of_three(arr, left, right)
        low = left
        high = right - 1
        while True:
            while arr[low] < pivot:
                low += 1
            while arr[high] > pivot:
                high -= 1
            if low < high:
                arr[low], arr[high] = arr[high], arr[low]
            else:
                break
        arr[low], arr[right - 1] = arr[right - 1], arr[low]
        quick_sort(arr, left, low - 1)
        quick_sort(arr, low + 1, right)
    else:
        insertion_sort(arr, left, right)

def quicksort(arr):
    quick_sort(arr, 0, len(arr) - 1)

Merge Sort

void mSort(ElementType[] A, ElementType[] tmpA, int L, int rightEnd) {
    // Core recursive sorting function
    int center;
    if (L < rightEnd) {
        center = (L + rightEnd) / 2;
        mSort(A, tmpA, L, center);              // Recursively sort the left half
        mSort(A, tmpA, center + 1, rightEnd);  // Recursively sort the right half
        merge(A, tmpA, L, center + 1, rightEnd); // Merge the two sorted halves
    }
}
void merge(ElementType[] A, ElementType[] tmpA, int L, int R, int rightEnd) {
    int leftEnd = R - 1; // Left subarray end index
    int tmp = L;         // Position for the sorted sequence in tmpA
    int numElements = rightEnd - L + 1;

    // Merge the two subarrays
    while (L <= leftEnd && R <= rightEnd) {
        if (A[L].compareTo(A[R]) <= 0) {
            tmpA[tmp++] = A[L++]; // Copy the left element to tmpA
        } else {
            tmpA[tmp++] = A[R++]; // Copy the right element to tmpA
        }
    }
    // Copy remaining elements from the left subarray, if any
    while (L <= leftEnd) {
        tmpA[tmp++] = A[L++];
    }
    // Copy remaining elements from the right subarray, if any
    while (R <= rightEnd) {
        tmpA[tmp++] = A[R++];
    }
    // Copy the sorted elements back to the original array A
    for (int i = 0; i < numElements; i++, rightEnd--) {
        A[rightEnd] = tmpA[rightEnd]; // Copy sorted tmpA back to A
    }
}

Merge Sort

def merge_sort(A):
    tmpA = [None] * len(A)
    m_sort(A, tmpA, 0, len(A) - 1)

def m_sort(A, tmpA, L, rightEnd):
    # Core recursive sorting function
    if L < rightEnd:
        center = (L + rightEnd) // 2
        m_sort(A, tmpA, L, center)              # Recursively sort the left half
        m_sort(A, tmpA, center + 1, rightEnd)   # Recursively sort the right half
        merge(A, tmpA, L, center + 1, rightEnd) # Merge the two sorted halves

def merge(A, tmpA, L, R, rightEnd):
    leftEnd = R - 1      # Left subarray end index
    tmp = L              # Position for the sorted sequence in tmpA
    numElements = rightEnd - L + 1

    # Merge the two subarrays
    while L <= leftEnd and R <= rightEnd:
        if A[L] <= A[R]:
            tmpA[tmp] = A[L]
            L += 1
        else:
            tmpA[tmp] = A[R]
            R += 1
        tmp += 1

    # Copy remaining elements from the left subarray, if any
    while L <= leftEnd:
        tmpA[tmp] = A[L]
        L += 1
        tmp += 1

    # Copy remaining elements from the right subarray, if any
    while R <= rightEnd:
        tmpA[tmp] = A[R]
        R += 1
        tmp += 1

    # Copy the sorted elements back to the original array A
    for i in range(numElements):
        A[rightEnd] = tmpA[rightEnd]
        rightEnd -= 1

Time Complexity

  • O(n^2): Insertion sort, Bubble sort, Selection sort
  • O(nlogn): QuickSort, MergeSort, HeapSort
  • O(n): Couting Sort, Bucket Sort, Radix Sort

Time Complexity

We need to consider about the best and worst case

For example:

Best case for Bubble Sort is O(n)

Worst case for QuickSort is O(n^2)

 

Question: How the quicksort hit the worst case?

What if the partition is always 1:100000?

Time Complexity

The lower bound of comparison sort is O(nlogn)

 

Counting Sort, Bucket Sort and Radix Sort are not comparison sort.

To get O(n), you need to have some pre defined requirements for the data you want to sort.

Counting, Bucket and Radix

  • These are not comparison sort
  • you have some limits on the numbers you are going to sort
  • Radix is just counting repeating multiple times

Space Complexity

  • In-place and out-of-place
  • in-place algorithm is an algorithm which transforms input using no auxiliary data structure

In-place: Bubble Sort, Insertion Sort, Selection Sort,                   QuickSort, HeapSort

Out-of place: Merge Sort, Counting Sort, Bucket Sort,                         Radix Sort

Stability

  • stable sorting algorithms maintain the relative order of records with equal keys

Stable: MergeSort, Bubble Sort, Insertion Sort,                        Counting Sort, Bucket Sort, Radix Sort

Unstable: QuickSort, HeapSort, Selection Sort

Sort algorithm Time complexity Space complexity Stability
Bubble O(n^2) O(1) YES
Insertion O(n^2) O(1) YES
Selection O(n^2) O(1) NO
Quick O(nlogn) O(logn) NO
Heap O(nlogn) O(1) NO
Merge O(nlogn) O(n) YES
Counting O(n+k) O(n+k) YES
Bucket O(n+k) O(n+k) YES
Radix O(dn) O(n) YES

External Sorting

  • The sort we talk about is internal sort
  • External sorting is required when the data being sorted do not fit into the main memory of a computing device and instead they must reside in the slower external memory
  • k-way merge
  • Instead of only considering the sorting algorithm, you need to count for the I/O time

What do we need to know

  • Some basic ideas on how these sorting algorithms work
  • Arrays.sort and Collections.sort
  • Comparator and Comparable

Largest Number

Given a list of non negative integers, arrange them such that they form the largest number.

For example, given [3, 30, 34, 5, 9], the largest formed number is 9534330.

Note: The result may be very large, so you need to return a string instead of an integer.

Largest Number

How to use sort to decide the largest number?

 

Example:

If we have 9 and 58. 958 > 589

  -> To sort them, we need to concat them and compare which is larger.

  -> This can apply to N numbers.

Largest Number

public String largestNumber(int[] nums) {
    if (nums == null || nums.length == 0) {
        return "";
    }
    String[] strs = new String[nums.length];
    for (int i = 0; i < nums.length; i++) {
        strs[i] = String.valueOf(nums[i]);
    }
    Arrays.sort(strs, new Comparator<String>(){
        public int compare(String str1, String str2) {
            return (str2 + str1).compareTo(str1 + str2);
        }
    });
    if (strs[0].charAt(0) == '0') {
        return "0";
    }
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < strs.length; i++) {
        result.append(strs[i]);
    }
    return result.toString();
}

Largest Number

    def largestNumber(self, nums: List[int]) -> str:
        if not nums:
            return ""
    
        # Convert the integers to strings
        strs = list(map(str, nums))
        
        # Sort strings based on custom comparator
        strs.sort(key=cmp_to_key(lambda x, y: 1 if x + y < y + x else -1))
        
        # If the largest number is "0", return "0"
        if strs[0] == '0':
            return "0"
        
        # Join sorted strings to form the largest number
        return ''.join(strs)
            

Best Meeting Point

A group of two or more people wants to meet and minimize the total travel distance. You are given a 2D grid of values 0 or 1, where each 1 marks the home of someone in the group. The distance is calculated using Manhattan Distance, where distance(p1, p2) = |p2.x - p1.x| + |p2.y - p1.y|.

For example, given three people living at (0,0), (0,4), and (2,2): Result is (0,2)

1 - 0 - 0 - 0 - 1
|   |   |   |   |
0 - 0 - 0 - 0 - 0
|   |   |   |   |
0 - 0 - 1 - 0 - 0

Best Meeting Point

First we consider the one dimension case:

 

_____A________________B_________

_____A______P_________B_________

_____A________________B____P____

 

As long as P is in the middle of A and B, the total distance is the same

______A________B________P________C_______D______

The total distance is AD + BC as long as P is in the middle

Best Meeting Point

So For two dimension, since it is Manhattan Distance, we can just take x and y separately

 

Pair first and last, second and second last ...

If there are 2N points, then P is in the middle of the median two.

If there are 2N+1 points, then P is just the N+1 point

Best Meeting Point

public int minTotalDistance(int[][] grid) {
    List<Integer> ipos = new ArrayList<Integer>();
    List<Integer> jpos = new ArrayList<Integer>();
    for(int i = 0; i < grid.length; i++){
        for(int j = 0; j < grid[0].length; j++){
            if(grid[i][j] == 1){
                ipos.add(i);
                jpos.add(j);
            }
        }
    }
    int sum = 0;
    Collections.sort(ipos);
    Collections.sort(jpos);
    int i = 0, j = ipos.size() - 1;
    while (i < j) {
        sum += ipos.get(j) - ipos.get(i);
        sum += jpos.get(j) - jpos.get(i);
        i ++;
        j --;
    }
    return sum;
}

Best Meeting Point


def min_total_distance(grid):
    ipos = []
    jpos = []

    # Collect row and column positions of all '1's
    for i in range(len(grid)):
        for j in range(len(grid[0])):
            if grid[i][j] == 1:
                ipos.append(i)
                jpos.append(j)
    
    # Sort the positions to get median-based meeting point
    ipos.sort()
    jpos.sort()

    # Calculate minimum total distance
    sum_distance = 0
    i, j = 0, len(ipos) - 1
    while i < j:
        sum_distance += ipos[j] - ipos[i]
        sum_distance += jpos[j] - jpos[i]
        i += 1
        j -= 1

    return sum_distance

Merge Intervals

Given a collection of intervals, merge all overlapping intervals.

 

For example,
Given [1,3],[2,6],[8,10],[15,18],
return [1,6],[8,10],[15,18].

Merge Intervals

We need to sort the intervals

Based on what? -> start

Go through all the results and merge them one by one

Merge Intervals

    public List<int[]> merge(int[][] intervals) {
        Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); // Sort intervals
        List<int[]> stack = new ArrayList<>();
        
        if (intervals.length == 0) return stack;

        for (int[] interval : intervals) {
            int l = interval[0];
            int r = interval[1];
            
            if (!stack.isEmpty()) {
                int[] prev = stack.get(stack.size() - 1);
                int prev_l = prev[0];
                int prev_r = prev[1];

                if (prev_r < l) {
                    stack.add(new int[] {l, r});
                } else {
                    prev[1] = Math.max(prev_r, r);
                }
            } else {
                stack.add(new int[] {l, r});
            }
        }
        
        return stack;
    }

Merge Intervals

    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        intervals.sort()
        stack = []
        if intervals == []: return stack

        for l, r in intervals:
            if stack:
                prev_l, prev_r = stack[-1]
                if prev_r < l:
                    stack.append([l, r])
                
                else:
                    stack[-1][1] = max(prev_r, r)
            else:
                stack.append([l, r])
        
        return stack

Kth Largest Element in an Array

    public int findKthLargest(int[] nums, int k) {
        return quickSelect(nums, 0, nums.length - 1, nums.length - k);
    }

    private int quickSelect(int[] nums, int start, int end, int target) {
        if (start >= end) {
            return nums[start];
        }

        int mid = (start + end) / 2;
        int pivot = nums[mid];

        int left = start, right = end;
        while (left <= right) {
            while (left <= right && nums[left] < pivot) {
                left++;
            }
            while (left <= right && nums[right] > pivot) {
                right--;
            }

            if (left <= right) {
                int temp = nums[left];
                nums[left] = nums[right];
                nums[right] = temp;
                left++;
                right--;
            }
        }

        if (target <= right) {
            return quickSelect(nums, start, right, target);
        } else if (target >= left) {
            return quickSelect(nums, left, end, target);
        } else {
            return nums[target];
        }
    }

Kth Largest Element in an Array

def findKthLargest(self, nums: List[int], k: int) -> int:
        
        def quickSelect(nums, start, end, target):
            if start >= end:
                return nums[start]
            
            mid = (start + end) // 2
            pivot = nums[mid]
            
            left, right = start, end
            while left <= right:
                while left <= right and nums[left] < pivot:
                    left += 1
                while left <= right and nums[right] > pivot:
                    right -= 1
                    
                if left <= right:
                    nums[left], nums[right] = nums[right], nums[left]
                    left += 1
                    right -= 1
                    
    
            if target <= right:
                return quickSelect(nums, start, right, target)
            elif target >= left:
                return quickSelect(nums, left, end, target)
            else:
                return nums[target]
            
        
        return quickSelect(nums, 0, len(nums) - 1, len(nums) - k)

Sort Colors

Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue.

We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively.

You must solve this problem without using the library's sort function.

 

Example 1:

Input: nums = [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]
Example 2:

Input: nums = [2,0,1]
Output: [0,1,2]

Sort Colors

    public void sortColors(int[] nums) {
        int n = nums.length;
        int p0 = 0;
        int p2 = n - 1;
        int cur = 0;
        
        while (cur <= p2) {
            int c = nums[cur];
            if (c == 0) {
                // Swap nums[cur] and nums[p0]
                int temp = nums[cur];
                nums[cur] = nums[p0];
                nums[p0] = temp;
                p0++;
            } else if (c == 2) {
                // Swap nums[cur] and nums[p2]
                int temp = nums[cur];
                nums[cur] = nums[p2];
                nums[p2] = temp;
                p2--;
                cur--;  // Move cur back to check the swapped element
            }
            cur++;
        }
    }

Sort Colors

    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        p0 = 0
        p2 = n - 1
        
        cur = 0
        
        while cur <= p2:
            c = nums[cur]
            if c == 0:
                nums[cur], nums[p0] = nums[p0], nums[cur]
                p0 += 1
            elif c == 2:
                nums[cur], nums[p2] = nums[p2], nums[cur]
                p2 -= 1
                cur -= 1 
            cur += 1
        return 

Homework

Copy of [GoValley-Jo] Sort 10

By ZhiTongGuiGu

Copy of [GoValley-Jo] Sort 10

  • 35