排序算法
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 */
各种排序算法性能比较:

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