Sorting

排序(Sorting)

  • 排序是最基本的算法
  • 排序是最常用的处理数据的方法之一.
    • 利用排序可以对数据进行去重等.

排序的种类

  • 基于比较的排序
    • 冒泡排序, 插入排序, 选择排序
    • 快速排序, 归并排序, 堆排序
  • 特殊的排序方法
    • 桶排序

冒泡排序(Bubble sort)

每次对相邻的数据进行比较, 看它们的顺序是否正确.

  • Best: O(n)
  • Average: O(n^2)
  • Worst: O(n^2)

Bubble sort

Alway compare adjacent items if they are in the wrong order.

  • Best: O(n^2)
  • Average: O(n^2)
  • Worst: O(n^2)
public void bubbleSort(int[] nums) {
    int n = nums.length;

    for (int i = 0; i < n; i++) {
        for (int j = 1; j < (n - i); j++) {
            if (nums[j - 1] > nums[j]) {
                int temp = nums[j - 1];
                nums[j - 1] = nums[j];
                nums[j] = temp;
            }
        }
    }
}

Bubble sort

Alway compare adjacent items if they are in the wrong order.

  • Best: O(n)
  • Average: O(n^2)
  • Worst: O(n^2)
public void bubbleSort(int[] nums) {
    int n = nums.length;

    for (int i = 0; i < n; i++) {
        boolean swapped = false;
        for (int j = 1; j < (n - i); j++) {
            if (nums[j - 1] > nums[j]) {
                int temp = nums[j - 1];
                nums[j - 1] = nums[j];
                nums[j] = temp;
                swapped = true;
            }
        }
        if (!swapped) return;
    }
}

插入排序(Insertion Sort)

每次把一张新的扑克牌, 插到已经排好序的一推扑克牌中 (同时需要不断地进行交换操作).

  • Best: O(n)
  • Average: O(n^2)
  • Worst: O(n^2)

Insertion Sort

Placing poker cards, insert card to the right place (with swap).

  • Best: O(n)
  • Average: O(n^2)
  • Worst: O(n^2)
public void insertSort(int[] nums){
    int n = nums.length;

    for (int i = 1; i < n; i++) {
        for(int j = i ; j > 0 ; j--) {
            if(nums[j] < nums[j-1]) {
                int temp = nums[j];
                nums[j] = nums[j-1];
                nums[j-1] = temp;
            }
        }
    }
    return input;
}

Insertion Sort

Placing poker cards, insert card to the right place (with swap).

  • Best: O(n)
  • Average: O(n^2)
  • Worst: O(n^2)
public void insertSort(int[] nums){
    int n = nums.length;

    for (int i = 1; i < n; i++) {
        int tmp = nums[i];
        int j = i;
        for (j = i ; j > 0 ; j--) {
            if (tmp < nums[j-1]) {
                nums[j] = nums[j-1];
            } else {
                break;
            }
        }
        nums[j] = tmp;
    }
    return input;
}

选择排序(Selection Sort)

每次选择一个最小的元素并取出, 重复此操作共N次.

  • Best: O(n^2)
  • Average: O(n^2)
  • Worst: O(n^2)

Selection Sort

Select minimum for N times.

  • Best: O(n^2)
  • Average: O(n^2)
  • Worst: O(n^2)
public void selectionSort(int[] nums){
    int n = nums.length;

    for (int i = 0; i < nums.length - 1; i++) {
        int min = nums[i];
        int minIdx = i;
        for (int j = i+1; j < nums.length; j++) {
            if (nums[j] < min) {
                min = nums[j];
                minIdx = j;
            }
        }
        nums[minIdx] = nums[i];
        nums[i] = min;
    }
}

归并排序(Merge Sort)

这是一种基于分治思想的排序算法, 并且需要采用递归来实现.

  • Best: O(nlogn)
  • Average: O(nlogn)
  • Worst: O(nlogn)

Merge Sort

Recursion. Divide and Conquer.

  • Best: O(nlogn)
  • Average: O(nlogn)
  • Worst: O(nlogn)
public void mergeSort(int[] nums){
    mergeSort(nums, 0, nums.length-1);
}

public void mergeSort(int[] nums, int begin, int end) {
    if (begin < end) {
        int mid = (begin + end) / 2;
        mergeSort(nums, begin, mid);
        mergeSort(nums, mid+1, end);
        merge(nums, begin, mid, end);
    }
}

public void merge(int[] nums,
                  int left, int leftEnd, int rightEnd);

快速排序(QuickSort)

快速排序也是基于分治的思想, 不断利用轴比较来进行数据的分割, 同样需要利用递归来实现.

  • Best: O(nlogn)
  • Average: O(nlogn)
  • Worst: O(n^2)

QuickSort

Recursion. Divide and conquer. PIVOT.

  • Best: O(nlogn)
  • Average: O(nlogn)
  • Worst: O(n^2)
public void quicksort(int[] nums, int begin, int end) {
    if (begin >= end) {
        return;
    }
    int pivotPostion = partition(nums, begin, end);
    quicksort(nums, begin, pivotPostion - 1);
    quicksort(nums, pivotPostion + 1, end);
}

QuickSort

Recursion. Divide and conquer. PIVOT.

  • Best: O(nlogn)
  • Average: O(nlogn)
  • Worst: O(n^2)
public int partition(int[] nums, int begin, int end) {
    int pivot = nums[begin];
    while (begin < end) {
        while (begin < end && nums[end] >= pivot) {
            end--;
        }
        nums[begin] = nums[end];
        while (begin < end && nums[begin] <= pivot) {
            begin++;
        }
        nums[end] = nums[begin];
    }
    nums[begin] = pivot;
    return begin;
}

桶排序(Bucket Sort)

  • 当要排序的数字都在某个特定范围内时, 可以统计在此范围内的每个数字各自出现的次数.

 

Example:

Given range: [1-10],

Given array:  [3, 5, 3, 2, 4, 1, 4, 9, 10],

=> Count: [1, 1, 2, 1, 1, 0, 0, 0, 1, 1]

=> Output: [1, 2, 3, 3, 4, 5, 9, 10], sorted

颜色排序(Sort Colors)

一个数组中的n个元素, 它们的颜色有红色, 白色或者蓝色. 对这n个元素进行排序使得颜色相同的元素全部相邻, 并且把颜色按照红色, 白色和蓝色的顺序进行排列.

我们用0, 1, 2这三个数字分别表示红色, 白色和蓝色.

Sort Colors

public void sortColors(int[] A) {

    int[] num = {0, 0, 0};
    for (int i = 0; i < A.length; i++) {
        num[A[i]]++;
    }
    for (int i = 0; i < num[0]; i++)
        A[i] = 0;
    for (int i = num[0]; i < num[0]+num[1]; i++)
        A[i] = 1;
    for (int i = num[0]+num[1]; i < A.length; i++)
        A[i] = 2;
}

合并区间(Merge Intervals)

给定一组区间, 合并所有重叠的区间.

 

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

Merge Intervals

public List<Interval> merge(List<Interval> intervals) {
    if (intervals.size() == 0)
        return new ArrayList<Interval>();
    Collections.sort(intervals, new Comparator<Interval>(){
        @Override
        public int compare(Interval a1, Interval a2) {
            return a1.start - a2.start;
        } 
    });
    
    ArrayList<Interval> results = new ArrayList<Interval>();
    Interval pre = intervals.get(0);
    for (int i = 1; i < intervals.size(); i++) {
        if (intervals.get(i).start <= pre.end) {
            pre.end = Math.max(intervals.get(i).end, pre.end);
        } else {
            results.add(new Interval(pre.start, pre.end));
            pre = intervals.get(i);
        }
    }
    results.add(pre);
    return results;
}
Best Average Worst
Bubble O(n) O(n^2) O(n^2)
Insertion O(n) O(n^2) O(n^2)
Selection O(n^2) O(n^2) O(n^2)
QuickSort O(nlogn) O(nlogn) O(n^2)
MergeSort O(nlogn) O(nlogn) O(nlogn)

排序算法的时间复杂度比较

Bonus: Insert Sort List

利用插入排序对链表进行排序.

public ListNode insertionSortList(ListNode head) {
    if (head == null)
        return head;
    ListNode dummy = new ListNode(-1);
    ListNode cur = head;
    while (cur != null) {
        ListNode pre = dummy;
        while (pre.next != null && pre.next.val <= cur.val) {
            pre = pre.next;
        }
        ListNode next = cur.next;
        cur.next = pre.next;
        pre.next = cur;
        cur = next;
    }
    return dummy.next;
}