diff --git a/notes/算法.md b/notes/算法.md index 18f46cf9..d2a1c63f 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -22,9 +22,6 @@ * [归并排序](#归并排序) * [快速排序](#快速排序) * [堆排序](#堆排序) - * [桶排序](#桶排序) - * [基数排序](#基数排序) - * [外部排序](#外部排序) * [小结](#小结) * [六、查找](#六查找) * [链表实现无序符号表](#链表实现无序符号表) @@ -631,14 +628,19 @@ public class WeightedQuickUnionUF extends UF { 使用辅助函数 less() 和 swap() 来进行比较和交换的操作,使得代码的可读性和可移植性更好。 ```java -private static boolean less(Comparable v, Comparable w) { - return v.compareTo(w) < 0; -} +public abstract class Sort> { -private static void swap(Comparable[] a, int i, int j) { - Comparable t = a[i]; - a[i] = a[j]; - a[j] = t; + public abstract void sort(T[] nums); + + protected boolean less(T v, T w) { + return v.compareTo(w) < 0; + } + + protected void swap(T[] a, int i, int j) { + T t = a[i]; + a[i] = a[j]; + a[j] = t; + } } ``` @@ -649,15 +651,16 @@ private static void swap(Comparable[] a, int i, int j) {

```java -public class Selection { - public static void sort(Comparable[] a) { - int N = a.length; +public class Selection> extends Sort { + @Override + public void sort(T[] nums) { + int N = nums.length; for (int i = 0; i < N; i++) { int min = i; for (int j = i + 1; j < N; j++) - if (less(a[j], a[min])) + if (less(nums[j], nums[min])) min = j; - swap(a, i, min); + swap(nums, i, min); } } } @@ -672,16 +675,17 @@ public class Selection { 在一轮交换中,如果没有发生交换,就说明数组已经是有序的,此时可以直接退出。 ```java -public class Bubble { - public static void sort(Comparable[] a) { - int N = a.length; +public class Bubble> extends Sort { + @Override + public void sort(T[] nums) { + int N = nums.length; boolean hasSorted = false; for (int i = 0; i < N && !hasSorted; i++) { hasSorted = true; for (int j = 0; j < N - i - 1; j++) { - if (less(a[j + 1], a[j])) { + if (less(nums[j + 1], nums[j])) { hasSorted = false; - swap(a, j, j + 1); + swap(nums, j, j + 1); } } } @@ -698,12 +702,13 @@ public class Bubble {

```java -public class Insertion { - public static void sort(Comparable[] a) { - int N = a.length; +public class Insertion> extends Sort { + @Override + public void sort(T[] nums) { + int N = nums.length; for (int i = 1; i < N; i++) - for (int j = i; j > 0 && less(a[j], a[j - 1]); j--) - swap(a, j, j - 1); + for (int j = i; j > 0 && less(nums[j], nums[j - 1]); j--) + swap(nums, j, j - 1); } } ``` @@ -727,17 +732,18 @@ public class Insertion {

```java -public class Shell { - public static void sort(Comparable[] a) { - int N = a.length; +public class Shell> extends Sort { + @Override + public void sort(T[] nums) { + int N = nums.length; int h = 1; while (h < N / 3) h = 3 * h + 1; // 1, 4, 13, 40, ... while (h >= 1) { for (int i = h; i < N; i++) - for (int j = i; j >= h && less(a[j], a[j - h]); j -= h) - swap(a, j, j - h); + for (int j = i; j >= h && less(nums[j], nums[j - h]); j -= h) + swap(nums, j, j - h); h = h / 3; } } @@ -757,24 +763,25 @@ public class Shell { 归并方法将数组中两个已经排序的部分归并成一个。 ```java -public class MergeSort { - private static Comparable[] aux; +public abstract class MergeSort> extends Sort { - private static void merge(Comparable[] a, int l, int m, int h) { + protected T[] aux; + + protected void merge(T[] nums, int l, int m, int h) { int i = l, j = m + 1; for (int k = l; k <= h; k++) - aux[k] = a[k]; // 将数据复制到辅助数组 + aux[k] = nums[k]; // 将数据复制到辅助数组 for (int k = l; k <= h; k++) { if (i > m) - a[k] = aux[j++]; + nums[k] = aux[j++]; else if (j > h) - a[k] = aux[i++]; - else if (aux[i].compareTo(a[j]) <= 0) - a[k] = aux[i++]; // 先进行这一步,保证稳定性 + nums[k] = aux[i++]; + else if (aux[i].compareTo(nums[j]) <= 0) + nums[k] = aux[i++]; // 先进行这一步,保证稳定性 else - a[k] = aux[j++]; + nums[k] = aux[j++]; } } } @@ -786,18 +793,21 @@ public class MergeSort { ```java -public static void sort(Comparable[] a) { - aux = new Comparable[a.length]; - sort(a, 0, a.length - 1); -} +public class Up2DownMergeSort> extends MergeSort { + @Override + public void sort(T[] nums) { + aux = (T[]) new Comparable[nums.length]; + sort(nums, 0, nums.length - 1); + } -private static void sort(Comparable[] a, int l, int h) { - if (h <= l) - return; - int mid = l + (h - l) / 2; - sort(a, l, mid); - sort(a, mid + 1, h); - merge(a, l, mid, h); + private void sort(T[] nums, int l, int h) { + if (h <= l) + return; + int mid = l + (h - l) / 2; + sort(nums, l, mid); + sort(nums, mid + 1, h); + merge(nums, l, mid, h); + } } ``` @@ -808,15 +818,16 @@ private static void sort(Comparable[] a, int l, int h) { 先归并那些微型数组,然后成对归并得到的微型数组。 ```java - public static void sort(Comparable[] a) { - int N = a.length; - aux = new Comparable[N]; - for (int sz = 1; sz < N; sz += sz) { - for (int lo = 0; lo < N - sz; lo += sz + sz) { - merge(a, lo, lo + sz - 1, Math.min(lo + sz + sz - 1, N - 1)); - } - } - } +public class Down2UpMergeSort> extends MergeSort { + @Override + public void sort(T[] nums) { + int N = nums.length; + aux = (T[]) new Comparable[N]; + for (int sz = 1; sz < N; sz += sz) + for (int lo = 0; lo < N - sz; lo += sz + sz) + merge(nums, lo, lo + sz - 1, Math.min(lo + sz + sz - 1, N - 1)); + } +} ``` ## 快速排序 @@ -829,24 +840,25 @@ private static void sort(Comparable[] a, int l, int h) {

```java -public class QuickSort { - public static void sort(Comparable[] a) { - shuffle(a); - sort(a, 0, a.length - 1); +public class QuickSort> extends Sort { + @Override + public void sort(T[] nums) { + shuffle(nums); + sort(nums, 0, nums.length - 1); } - private static void sort(Comparable[] a, int l, int h) { + private void sort(T[] nums, int l, int h) { if (h <= l) return; - int j = partition(a, l, h); - sort(a, l, j - 1); - sort(a, j + 1, h); + int j = partition(nums, l, h); + sort(nums, l, j - 1); + sort(nums, j + 1, h); } - private static void shuffle(Comparable[] array) { - List list = Arrays.asList(array); + private void shuffle(T[] nums) { + List list = Arrays.asList(nums); Collections.shuffle(list); - list.toArray(array); + list.toArray(nums); } } ``` @@ -858,17 +870,17 @@ public class QuickSort {

```java -private static int partition(Comparable[] a, int l, int h) { +private int partition(T[] nums, int l, int h) { int i = l, j = h + 1; - Comparable v = a[l]; + T v = nums[l]; while (true) { - while (less(a[++i], v) && i != h) ; - while (less(v, a[--j]) && j != l) ; + while (less(nums[++i], v) && i != h) ; + while (less(v, nums[--j]) && j != l) ; if (i >= j) break; - swap(a, i, j); + swap(nums, i, j); } - swap(a, l, j); + swap(nums, l, j); return j; } ``` @@ -898,27 +910,24 @@ private static int partition(Comparable[] a, int l, int h) { 三向切分快速排序对于只有若干不同主键的随机数组可以在线性时间内完成排序。 ```java -public class Quick3Way { - public static void sort(Comparable[] a) { - sort(a, 0, a.length - 1); - } - - private static void sort(Comparable[] a, int l, int h) { +public class ThreeWayQuickSort> extends QuickSort { + @Override + protected void sort(T[] nums, int l, int h) { if (h <= l) return; int lt = l, i = l + 1, gt = h; - Comparable v = a[l]; + T v = nums[l]; while (i <= gt) { - int cmp = a[i].compareTo(v); + int cmp = nums[i].compareTo(v); if (cmp < 0) - swap(a, lt++, i++); + swap(nums, lt++, i++); else if (cmp > 0) - swap(a, i, gt--); + swap(nums, i, gt--); else i++; } - sort(a, l, lt - 1); - sort(a, gt + 1, h); + sort(nums, l, lt - 1); + sort(nums, gt + 1, h); } } ``` @@ -930,18 +939,18 @@ public class Quick3Way { 可以利用这个特性找出数组的第 k 个元素。 ```java -public static Comparable select(Comparable[] a, int k) { - int l = 0, h = a.length - 1; +public T select(T[] nums, int k) { + int l = 0, h = nums.length - 1; while (h > l) { - int j = partition(a, l, h); + int j = partition(nums, l, h); if (j == k) - return a[k]; + return nums[k]; else if (j > k) h = j - 1; else l = j + 1; } - return a[k]; + return nums[k]; } ``` @@ -958,13 +967,13 @@ public static Comparable select(Comparable[] a, int k) {

```java -public class Heap { - private Comparable[] heap; +public class Heap> { + + private T[] heap; private int N = 0; public Heap(int maxN) { - heap = new Comparable[maxN + 1]; - N = maxN; + this.heap = (T[]) new Comparable[maxN + 1]; } public boolean isEmpty() { @@ -980,7 +989,7 @@ public class Heap { } private void swap(int i, int j) { - Comparable t = heap[i]; + T t = heap[i]; heap[i] = heap[j]; heap[j] = t; } @@ -1036,8 +1045,8 @@ public void insert(Comparable v) { 从数组顶端删除最大的元素,并将数组的最后一个元素放到顶端,并让这个元素下沉到合适的位置。 ```java -public Comparable delMax() { - Comparable max = heap[1]; +public T delMax() { + T max = heap[1]; swap(1, N--); heap[N + 1] = null; sink(1); @@ -1065,7 +1074,7 @@ public Comparable delMax() { ```java public class HeapSort { - public static void sort(Comparable[] a) { // 数组第 0 个位置不能有元素 + public static void sort(Comparable[] a) { int N = a.length - 1; for (int k = N / 2; k >= 0; k--) sink(a, k, N); @@ -1101,25 +1110,20 @@ public class HeapSort { 现代操作系统很少使用堆排序,因为它无法利用缓存,也就是数组元素很少和相邻的元素进行比较。 -## 桶排序 - -## 基数排序 - -## 外部排序 - ## 小结 ### 1. 排序算法的比较 -| 算法 | 稳定 | 原地排序 | 时间复杂度 | 空间复杂度 | 备注 | -| :---: | :---: | :---: | :---: | :---: | :---: | -| 选择排序 | no | yes | N2 | 1 | | -| 插入排序 | yes | yes | N \~ N2 | 1 | 时间复杂度和初始顺序有关 | -| 希尔排序 | no | yes | N 的若干倍乘于递增序列的长度 | 1 | | -| 快速排序 | no | yes | NlogN | logN | | -| 三向切分快速排序 | no | yes | N \~ NlogN | logN | 适用于有大量重复主键| -| 归并排序 | yes | no | NlogN | N | | -| 堆排序 | no | yes | NlogN | 1 | | | +| 算法 | 稳定 | 时间复杂度 | 空间复杂度 | 备注 | +| :---: | :---: |:---: | :---: | :---: | +| 选择排序 | no | N2 | 1 | | +| 冒泡排序 | yes | N2 | 1 | | +| 插入排序 | yes | N \~ N2 | 1 | 时间复杂度和初始顺序有关 | +| 希尔排序 | no | N 的若干倍乘于递增序列的长度 | 1 | | +| 快速排序 | no | NlogN | logN | | +| 三向切分快速排序 | no | N \~ NlogN | logN | 适用于有大量重复主键| +| 归并排序 | yes | NlogN | N | | +| 堆排序 | no | NlogN | 1 | | | 快速排序是最快的通用排序算法,它的内循环的指令很少,而且它还能利用缓存,因为它总是顺序地访问数据。它的运行时间近似为 \~cNlogN,这里的 c 比其他线性对数级别的排序算法都要小。使用三向切分快速排序,实际应用中可能出现的某些分布的输入能够达到线性级别,而其它排序算法仍然需要线性对数时间。