排序算法

a. 排序算法的基本思想

b. 排序算法的过程 

c. 排序算法的性能(时间复杂度、空间复杂度、稳定性) 

稳定性:保证排序前后两个相等的数的相对顺序不变。 
依次比较的是稳定的,跳跃比较的是不稳定的(步子迈大了,容易扯着蛋,哈哈哈就是这个意思)。

插入类排序 

插入排序的基本思想是:在一个已排好序的记录子集的基础上,每一步把下一个待排序的记录有序地插入到已排好序的记录子集中,直到所有待排记录被全部插入为止。 
打扑克时的抓牌就是插入排序的一个典型情境,每抓一张牌,插入到合适位置,直到抓完牌为止,即可得到一个有序序列。

1. 直接插入排序

 1 private static int[] insertSort(int[] arr) {
 2     if (arr == null || arr.length < 2) {
 3         return arr;
 4     }
 5     for (int i = 1; i < arr.length; i++) {
 6         for (int j = i; j > 0; j--) {
 7             if (arr[j] < arr[j - 1]) {
 8                 int t = arr[j];
 9                 arr[j] = arr[j - 1];
10                 arr[j - 1] = t;
11             } else {
12                 break;
13             }
14         }
15     }
16     return arr;
17 }

最好情况,待排序数组已经是升序状态,时间复杂度为O(N) 
最坏情况,待排序数组为反序状态,时间复杂度为O(N^2) 
平均来说,插入排序的时间复杂度为O(N^2) 
空间复杂度为O(1),是稳定排序 
比较适合待排序数组长度较小且基本有序的情况

2. 希尔排序

基本思想:把待排序数组分割成若干个较稀疏的子序列,分别进行直接插入排序。经过上述粗略调整,整个序列中的记录已经基本有序,最后再对全部记录进行一次直接插入排序。

 1 public static void shellSort(int[] arr) {
 2     for (int gap = arr.length / 2; gap > 0; gap /= 2) {
 3         for (int i = gap; i < arr.length; i++) {
 4             int j = i;
 5             int t = arr[j];
 6             if (arr[j] < arr[j - gap]) {
 7                 while (j - gap >= 0 && t < arr[j - gap]) {
 8                     arr[j] = arr[j - gap];
 9                     j -= gap;
10                 }
11                 arr[j] = t;
12             }
13         }
14     }
15 }

最坏情况,时间复杂度为O(N^2) 
最好情况,时间复杂度为O(N^1.3) 
空间复杂度为O(1),是不稳定排序

交换类排序

1. 冒泡排序 

 1 public static void bubbleSort(int[] a) {
 2     int t = 0;
 3     for (int i = a.length - 1; i > 0; --i) {
 4         for (int j = 0; j < i; ++j) {
 5             if (a[j + 1] < a[j]) {
 6                 t = a[j];
 7                 a[j] = a[j + 1];
 8                 a[j + 1] = t;
 9             }
10         }
11     }
12 }

最好情况,已经升序,时间复杂度为O(N) 
最坏情况和平均情况的时间复杂度都是O(N^2) 
空间复杂度为O(1),是稳定排序

2. 快速排序

 1 public static void quickSort(int[] arr, int start, int end) {
 2     if (start < end) {
 3         int index = partition(arr, start, end);
 4         quickSort(arr, start, index - 1);
 5         quickSort(arr, index + 1, end);
 6     }
 7 }
 8 
 9 private static void quickSortByLoop(int[] a, int start, int end) {
10     Stack<Integer> stack = new Stack<>();
11     if (start < end) {
12         stack.push(end);
13         stack.push(start);
14         while (!stack.isEmpty()) {
15             int l = stack.pop();
16             int r = stack.pop();
17             int index = partition(a, l, r);
18             if (l < index - 1) {
19                 stack.push(index - 1);
20                 stack.push(l);
21             }
22             if (r > index + 1) {
23                 stack.push(r);
24                 stack.push(index + 1);
25             }
26         }
27     }
28 }
29 
30 private static int partition(int[] arr, int start, int end) {
31     int pivot = arr[start];
32     while (start < end) {
33         while (start < end && arr[end] >= pivot) end--;
34         arr[start] = arr[end];
35         while (start < end && arr[start] <= pivot) start++;
36         arr[end] = arr[start];
37     }
38     arr[start] = pivot;
39     return start;
40 }

最坏情况出现在待排序数组已经为升序状态,时间复杂度为O(N^2) 
最好情况和平均情况的时间复杂度为O(NlogN) 
空间复杂度为O(logN)~O(N),是不稳定排序

选择类排序 

1. 简单选择排序 

 1 public static void selectSort(int[] a) {
 2     for (int i = 0; i < a.length; i++) {
 3         int minIndex = i;
 4         for (int j = i + 1; j < a.length; j++) {
 5             if (a[j] < a[minIndex])
 6                 minIndex = j;
 7         }
 8         if (minIndex != i) {
 9             int t = a[i];
10             a[i] = a[minIndex];
11             a[minIndex] = t;
12         }
13     }
14 }

最好最快平均情况的时间复杂度都是O(N^2) 
空间复杂度为O(1),是否为稳定排序有争议,大部分人认为是不稳定的,[2,2,1]

2. 堆排序

大顶堆:任何非叶子节点的值都大于它的左右孩子的值。 
建堆:从叶子节点找出最大值不断的往上一层冒,最终把最大值冒到顶部根的位置。 

 1 public class HeapSort {
 2     private static int[] sort = new int[]{1, 0, 10, 20, 3, 5, 6, 4, 9, 8, 12, 17, 34, 11};
 3 
 4     public static void main(String[] args) {
 5         buildMaxHeapify(sort);
 6         heapSort(sort);
 7         print(sort);
 8     }
 9 
10     private static void buildMaxHeapify(int[] data) {
11         int startIndex = getParentIndex(data.length - 1);
12         for (int i = startIndex; i >= 0; i--) {
13             maxHeapify(data, data.length, i);
14         }
15     }
16 
17     private static void maxHeapify(int[] data, int heapSize, int index) {
18         int left = getChildLeftIndex(index);
19         int right = getChildRightIndex(index);
20         int largest = index;
21         if (left < heapSize && data[index] < data[left]) {
22             largest = left;
23         }
24         if (right < heapSize && data[largest] < data[right]) {
25             largest = right;
26         }
27         if (largest != index) {
28             int temp = data[index];
29             data[index] = data[largest];
30             data[largest] = temp;
31             maxHeapify(data, heapSize, largest);
32         }
33     }
34 
35     private static void heapSort(int[] data) {
36         for (int i = data.length - 1; i > 0; i--) {
37             int temp = data[0];
38             data[0] = data[i];
39             data[i] = temp;
40             maxHeapify(data, i, 0);
41         }
42     }
43 
44     private static int getParentIndex(int current) {
45         return (current - 1) >> 1;
46     }
47 
48     private static int getChildLeftIndex(int current) {
49         return (current << 1) + 1;
50     }
51 
52     private static int getChildRightIndex(int current) {
53         return (current << 1) + 2;
54     }
55 
56     private static void print(int[] data) {
57         int pre = -2;
58         for (int i = 0; i < data.length; i++) {
59             if (pre < (int) getLog(i + 1)) {
60                 pre = (int) getLog(i + 1);
61                 System.out.println();
62             }
63             System.out.print(data[i] + "|");
64         }
65     }
66 
67     private static double getLog(double param) {
68         return Math.log(param) / Math.log(2);
69     }
70 }
71 /*
72 0|
73 1|3|
74 4|5|6|8|
75 9|10|11|12|17|20|34|
76  */

最好最坏平均情况的时间复杂度都是O(NlogN) 
空间复杂度为O(1),是不稳定排序


1. 归并排序

public class Merge {
    public static void main(String[] args) {
        int[] a = {1, 0, 10, 20, 3, 5, 6, 4, 9, 8, 12, 17, 34, 11};
        sort(a);
        for (int x : a)
            System.out.print(x + " ");
    }

    private static int[] aux;

    public static void sort(int[] a) {
        aux = new int[a.length];
        sort(a, 0, a.length - 1);
    }

    private static void sort(int[] a, int lo, int hi) {
        if (hi <= lo) return;
        int mid = lo + (hi - lo) / 2;
        sort(a, lo, mid);
        sort(a, mid + 1, hi);
        merge(a, lo, mid, hi);
    }

    public static void merge(int[] a, int lo, int mid, int hi) {
        int i = lo, j = mid + 1;
        for (int k = lo; k <= hi; k++)
            aux[k] = a[k];
        for (int k = lo; k <= hi; k++)
            if (i > mid) a[k] = aux[j++];
            else if (j > hi) a[k] = aux[i++];
            else if (aux[j] < aux[i]) a[k] = aux[j++];
            else a[k] = aux[i++];
    }
}
/*
0 1 3 4 5 6 8 9 10 11 12 17 20 34 
 */

最好最坏平均情况的时间复杂度都是O(NlogN) 
空间复杂度为O(N),是稳定排序

2. 基数排序 

 1 public class RadixSort {
 2     public static void sort(int[] number, int d) {
 3         int k = 0;
 4         int n = 1;
 5         int m = 1;
 6         int[][] temp = new int[10][number.length];
 7         int[] order = new int[10];
 8         while (m <= d) {
 9             for (int i = 0; i < number.length; i++) {
10                 int lsd = ((number[i] / n) % 10);
11                 temp[lsd][order[lsd]] = number[i];
12                 order[lsd]++;
13             }
14             for (int i = 0; i < 10; i++) {
15                 if (order[i] != 0)
16                     for (int j = 0; j < order[i]; j++) {
17                         number[k] = temp[i][j];
18                         k++;
19                     }
20                 order[i] = 0;
21             }
22             n *= 10;
23             k = 0;
24             m++;
25         }
26     }
27 
28     public static void main(String[] args) {
29         int[] data = {73, 22, 93, 43, 55, 14, 28, 65, 39, 81, 33, 100};
30         RadixSort.sort(data, 3);
31         for (int i = 0; i < data.length; i++) {
32             System.out.print(data[i] + " ");
33         }
34     }
35 }
36 /*
37 14 22 28 33 39 43 55 65 73 81 93 100
38  */

3. 计数排序 

 1 public class CountSort {
 2     public static void main(String[] args) {
 3         int a[] = {100, 93, 97, 92, 96, 99, 92, 89, 93, 97, 90, 94, 92, 95};
 4         int b[] = countSort(a);
 5         for (int i : b)
 6             System.out.print(i + " ");
 7     }
 8 
 9     public static int[] countSort(int[] a) {
10         int b[] = new int[a.length];
11         int max = a[0], min = a[0];
12         for (int i : a) {
13             if (i > max) {
14                 max = i;
15             }
16             if (i < min) {
17                 min = i;
18             }
19         }
20         int k = max - min + 1;
21         int c[] = new int[k];
22         for (int i = 0; i < a.length; ++i) {
23             c[a[i] - min] += 1;
24         }
25         for (int i = 1; i < c.length; ++i) {
26             c[i] = c[i] + c[i - 1];
27         }
28         for (int i = a.length - 1; i >= 0; --i) {
29             b[--c[a[i] - min]] = a[i];
30         }
31         return b;
32     }
33 }
34 /*
35 89 90 92 92 92 93 93 94 95 96 97 97 99 100 
36  */

各种排序算法性能比较:


外排序 

第一阶段是把文件逐段输入到内存,用较好的内排序方法对这段文件进行排序。已排序的文件段通常称为归并段。整个文件经过逐段排序后又逐段写回到外存上。这样,在外存上就形成了许多初始归并段。 
第二阶段是对这些初始归并段使用某种归并方法(如两路归并法),进行多遍归并。最后在外存上形成一个排序的文件。

posted @ 2018-03-13 14:34  sakura1027  阅读(246)  评论(0)    收藏  举报