排序是将一个无序的序列按照一定顺序排列成一个有序序列的操作。其中,升序是指序列按从小到大排序,降序是指序列按从大到小排序。常见的排序算法有:冒泡排序、快速排序、选择排序、插入排序、归并排序、堆排序等。
若一个序列中存在相同的两个元素,在排序后:
a.若这两个元素先后顺序不变,则称该排序算法是稳定的。
b.若这两个元素先后顺序变化,则称该排序算法是不稳定的。
例如:对于序列(56, 34, 47, 23, 66, 18, 82, 47)中有两个47,在第一个47下划线。
若排序结果为(18, 23, 34, 47, 47, 56, 66, 82),即两个47的先后顺序不变,则该排序算法是稳定的;
若排序结果为(18, 23, 34, 47, 47, 56, 66, 82),即两个47的先后顺序变化,则该排序算法是不稳定的。
Method枚举类定义的是排列方式,存在两个值:ASC(升序)和DESC(降序)。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 * 排序方式 3 */ 4 enum Method { 5 /** 6 * 升序 7 */ 8 ASC, 9 10 /** 11 * 降序 12 */ 13 DESC; 14 }
swap方法是将数组中两个元素互换位置。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 * 将数组中两个元素互换位置 3 * @param arr 4 * @param i 5 * @param j 6 */ 7 private static void swap(int[] arr, int i, int j) { 8 int temp = arr[i]; 9 arr[i] = arr[j]; 10 arr[j] = temp; 11 }
冒泡排序
冒泡排序是将序列中相邻两个元素进行比较,若为逆序,则交换位置。
一趟升序的冒泡排序的过程为:序列第一个元素与第二个元素进行比较,小的在前大的在后,接着第二个元素与第三个元素比较,也是小的在前大的在后......直到最后两个元素比较交换位置完。
一趟降序的冒泡排序的过程为:序列第一个元素与第二个元素进行比较,大的在前小的在后,接着第二个元素与第三个元素比较,也是大的在前小的在后......直到最后两个元素比较交换位置完。
冒泡排序的代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 * 冒泡排序 3 * @param arr 4 * @param method 排序方式 5 * @return 6 */ 7 public static int[] bubble(int[] arr, Method method) { 8 int[] a = Arrays.copyOf(arr, arr.length); 9 if (method == Method.ASC) { // 升序 10 for (int j = a.length; j > 1; j--) { 11 for (int i = 0; i < j - 1; i++) { 12 /** 13 * a[i] > a[i + 1]:该排序算法是稳定的 14 * a[i] >= a[i + 1]:该排序算法是不稳定的 15 */ 16 if (a[i] > a[i + 1]) { 17 swap(a, i, i + 1); 18 } 19 } 20 } 21 } else { // 降序 22 for (int j = a.length; j > 1; j--) { 23 for (int i = 0; i < j - 1; i++) { 24 /** 25 * a[i] < a[i + 1]:该排序算法是稳定的 26 * a[i] <= a[i + 1]:该排序算法是不稳定的 27 */ 28 if (a[i] < a[i + 1]) { 29 swap(a, i, i + 1); 30 } 31 } 32 } 33 } 34 return a; 35 }
例如:对序列(5, 8, 3, 4, 9, 2, 7, 6, 1)进行升序排序。
第一趟冒泡排序的过程为:
每一趟冒泡排序的结果为:
[5, 8, 3, 4, 9, 2, 7, 6, 1] --> [5, 3, 4, 8, 2, 7, 6, 1, 9] --> [3, 4, 5, 2, 7, 6, 1, 8, 9] -->
[3, 4, 2, 5, 6, 1, 7, 8, 9] --> [3, 2, 4, 5, 1, 6, 7, 8, 9] --> [2, 3, 4, 1, 5, 6, 7, 8, 9] -->
[2, 3, 1, 4, 5, 6, 7, 8, 9] --> [2, 1, 3, 4, 5, 6, 7, 8, 9] --> [1, 2, 3, 4, 5, 6, 7, 8, 9]
快速排序
快速排序是从序列中选定一个元素作为枢轴,将其他元素与枢轴进行比较,分为比枢轴小和比枢轴大两个子序列。快速排序算法是不稳定的。
一趟升序的快速排序的过程为:定义两个指针low和high指向序列两端,并取low指向的元素作为枢轴。首先是high往前移,直到找到比枢轴小的数,将该数赋给low指向的位置;然后low往后移,直到找到比枢轴大的数,将该数赋给high指向的位置;接着又是high往前移......直到最后low和high指向同一个位置,将枢轴赋给该位置。
一趟降序的快速排序的过程为:定义两个指针low和high指向序列两端,并取low指向的元素作为枢轴。首先是high往前移,直到找到比枢轴大的数,将该数赋给low指向的位置;然后low往后移,直到找到比枢轴小的数,将该数赋给high指向的位置;接着又是high往前移......直到最后low和high指向同一个位置,将枢轴赋给该位置。
快速排序的代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 * 快速排序 3 * @param arr 4 * @param method 排序方式 5 * @return 6 */ 7 public static int[] quick(int[] arr, Method method) { 8 int[] a = Arrays.copyOf(arr, arr.length); 9 qSort(a, 0, a.length - 1, method); 10 return a; 11 } 12 13 private static void qSort(int[] a, int low, int high, Method method) { 14 if (low < high) { 15 int pivotloc = partition(a, low, high, method); 16 qSort(a, low, pivotloc - 1, method); 17 qSort(a, pivotloc + 1, high, method); 18 } 19 } 20 21 private static int partition(int[] a, int low, int high, Method method) { 22 int pivot = a[low]; // 取low位置的数作为枢轴 23 if (method == Method.ASC) { // 升序 24 while (low < high) { 25 // 大数放右边 26 while (low < high && a[high] > pivot) high--; 27 a[low] = a[high]; 28 // 小数放左边 29 while (low < high && a[low] < pivot) low++; 30 a[high] = a[low]; 31 } 32 } else { // 降序 33 while (low < high) { 34 // 小数放右边 35 while (low < high && a[high] < pivot) high--; 36 a[low] = a[high]; 37 // 大数放左边 38 while (low < high && a[low] > pivot) low++; 39 a[high] = a[low]; 40 } 41 } 42 a[low] = pivot; 43 return low; 44 }
例如:对序列(5, 8, 3, 4, 9, 2, 7, 6, 1)进行升序排序。
第一趟快速排序的过程为:
每一趟快速排序的结果为:
[5, 8, 3, 4, 9, 2, 7, 6, 1] --> [1, 2, 3, 4, 5, 9, 7, 6, 8] --> [1, 2, 3, 4, 5, 9, 7, 6, 8] -->
[1, 2, 3, 4, 5, 9, 7, 6, 8] --> [1, 2, 3, 4, 5, 9, 7, 6, 8] --> [1, 2, 3, 4, 5, 8, 7, 6, 9] -->
[1, 2, 3, 4, 5, 6, 7, 8, 9]
选择排序
选择排序是每次从序列中选择最值放在一端。
一趟升序的选择排序的过程为:从序列中选择最大的元素放在最末端。
一趟降序的选择排序的过程为:从序列中选择最小的元素放在最末端。
选择排序的代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 * 选择排序 3 * @param arr 4 * @param method 排序方式 5 * @return 6 */ 7 public static int[] select(int[] arr, Method method) { 8 int[] a = Arrays.copyOf(arr, arr.length); 9 if (method == Method.ASC) { // 升序 10 for (int i = a.length - 1; i > 0; i--) { 11 int index = getMaxIndex(a, i); 12 if (index != i) { 13 swap(a, index, i); 14 } 15 } 16 } else { // 降序 17 for (int i = a.length - 1; i > 0; i--) { 18 int index = getMinIndex(a, i); 19 if (index != i) { 20 swap(a, index, i); 21 } 22 } 23 } 24 return a; 25 } 26 27 private static int getMaxIndex(int[] arr, int end) { 28 int index = 0; 29 int num = arr[0]; 30 for (int i = 1; i <= end; i++) { 31 /** 32 * num < arr[i]:该排序算法是稳定的 33 * num <= arr[i]:该排序算法是不稳定的 34 */ 35 if (num < arr[i]) { 36 index = i; 37 num = arr[i]; 38 } 39 } 40 return index; 41 } 42 43 private static int getMinIndex(int[] arr, int end) { 44 int index = 0; 45 int num = arr[0]; 46 for (int i = 1; i <= end; i++) { 47 /** 48 * num > arr[i]:该排序算法是稳定的 49 * num >= arr[i]:该排序算法是不稳定的 50 */ 51 if (num > arr[i]) { 52 index = i; 53 num = arr[i]; 54 } 55 } 56 return index; 57 }
例如:对序列(5, 8, 3, 4, 9, 2, 7, 6, 1)进行升序排序。
第一趟选择排序的过程为:
每一趟选择排序的结果为:
[5, 8, 3, 4, 9, 2, 7, 6, 1] --> [5, 8, 3, 4, 1, 2, 7, 6, 9] --> [5, 6, 3, 4, 1, 2, 7, 8, 9] -->
[5, 6, 3, 4, 1, 2, 7, 8, 9] --> [5, 2, 3, 4, 1, 6, 7, 8, 9] --> [1, 2, 3, 4, 5, 6, 7, 8, 9] -->
[1, 2, 3, 4, 5, 6, 7, 8, 9] --> [1, 2, 3, 4, 5, 6, 7, 8, 9] --> [1, 2, 3, 4, 5, 6, 7, 8, 9]
插入排序
插入排序是找到第一个逆序的数,将该数插入到前面合适的位置。
一趟升序的插入排序的过程为:找到第一个比其前一位小的数,将该数插在前面序列合适的位置。
一趟降序的插入排序的过程为:找到第一个比其前一位大的数,将该数插在前面序列合适的位置。
插入排序的代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 * 插入排序 3 * @param arr 4 * @param method 排序方式 5 * @return 6 */ 7 public static int[] insert(int[] arr, Method method) { 8 int[] a = Arrays.copyOf(arr, arr.length); 9 if (method == Method.ASC) { // 升序 10 for (int i = 0; i < a.length - 1; i++) { 11 if (a[i + 1] < a[i]) { 12 int guard = a[i + 1]; 13 a[i + 1] = a[i]; 14 int j = i; 15 /** 16 * guard < a[j - 1]:该排序算法是稳定的 17 * guard <= a[j - 1]:该排序算法是不稳定的 18 */ 19 while (j > 0 && guard < a[j - 1]) { 20 a[j] = a[j - 1]; 21 j--; 22 } 23 a[j] = guard; 24 } 25 } 26 } else { // 降序 27 for (int i = 0; i < a.length - 1; i++) { 28 if (a[i + 1] > a[i]) { 29 int guard = a[i + 1]; 30 a[i + 1] = a[i]; 31 int j = i; 32 /** 33 * guard > a[j - 1]:该排序算法是稳定的 34 * guard >= a[j - 1]:该排序算法是不稳定的 35 */ 36 while (j > 0 && guard > a[j - 1]) { 37 a[j] = a[j - 1]; 38 j--; 39 } 40 a[j] = guard; 41 } 42 } 43 } 44 return a; 45 }
例如:对序列(5, 8, 3, 4, 9, 2, 7, 6, 1)进行升序排序。
第一趟插入排序的过程为:
每一趟插入排序的结果为:
[5, 8, 3, 4, 9, 2, 7, 6, 1] --> [3, 5, 8, 4, 9, 2, 7, 6, 1] --> [3, 4, 5, 8, 9, 2, 7, 6, 1] -->
[2, 3, 4, 5, 8, 9, 7, 6, 1] --> [2, 3, 4, 5, 7, 8, 9, 6, 1] --> [2, 3, 4, 5, 6, 7, 8, 9, 1] -->
[1, 2, 3, 4, 5, 6, 7, 8, 9]
归并排序
归并排序是指把序列递归分解成若干个长度大致相等的子序列,然后将各个子序列合并,并在合并时进行排序,最终合并为整体有序序列的过程。
归并排序的代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 * 归并排序 3 * @param arr 4 * @param method 5 * @return 排序方式 6 */ 7 public static int[] merge(int[] arr, Method method) { 8 int[] a = Arrays.copyOf(arr, arr.length); 9 merge1(a, 0, a.length - 1, method); 10 return a; 11 } 12 13 private static void merge1(int[] arr, int begin, int end, Method method) { 14 if (begin == end) return; 15 int m = (begin + end + 1) / 2; 16 merge1(arr, begin, m - 1, method); 17 merge1(arr, m, end, method); 18 merge2(arr, begin, m, end, method); 19 } 20 21 private static void merge2(int[] arr, int begin, int m, int end, Method method) { 22 int[] a = new int[end + 1 - begin]; 23 int i = begin, j = m, k = 0; 24 if (method == Method.ASC) { 25 while (i < m && j <= end) { 26 /** 27 * arr[i] <= arr[j]:该排序算法是稳定的 28 * arr[i] < arr[j]:该排序算法是不稳定的 29 */ 30 if (arr[i] <= arr[j]) { 31 a[k++] = arr[i++]; 32 } else { 33 a[k++] = arr[j++]; 34 } 35 } 36 } else { 37 while (i < m && j <= end) { 38 /** 39 * arr[i] >= arr[j]:该排序算法是稳定的 40 * arr[i] > arr[j]:该排序算法是不稳定的 41 */ 42 if (arr[i] >= arr[j]) { 43 a[k++] = arr[i++]; 44 } else { 45 a[k++] = arr[j++]; 46 } 47 } 48 } 49 while (i < m) a[k++] = arr[i++]; 50 while (j <= end) a[k++] = arr[j++]; 51 System.arraycopy(a, 0, arr, begin, a.length); 52 }
例如:对序列(5, 8, 3, 4, 9, 2, 7, 6, 1)进行升序排序。
归并排序的过程为:
堆排序
堆排序是利用堆的特性进行排序的一种排序方法:
大顶堆:堆顶是最大的元素,堆顶的两个子结点中较大的是第二大的元素。
小顶堆:堆顶是最小的元素,堆顶的两个子结点中较小的是第二小的元素。
堆排序算法是不稳定的。
一趟升序的堆排序的过程为:将大顶堆堆顶的两个子结点中较大的结点与堆尾的前一个结点交换,将堆顶与堆尾交换,然后移除最后两个结点,最后对交换过的结点重新调整为大顶堆。
一趟降序的堆排序的过程为:将小顶堆堆顶的两个子结点中较小的结点与堆尾的前一个结点交换,将堆顶与堆尾交换,然后移除最后两个结点,最后对交换过的结点重新调整为小顶堆。
堆排序的代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /** 2 * 堆排序 3 * @param arr 4 * @param method 排序方式 5 * @return 6 */ 7 public static int[] heap(int[] arr, Method method) { 8 int[] a = makeHeap(arr, method); 9 for (int i = a.length; i > 1; i -= 2) { 10 heap(a, i, method); 11 } 12 return a; 13 } 14 15 @SuppressWarnings("preview") 16 private static int[] makeHeap(int[] arr, Method method) { 17 int[] heap = new int[arr.length]; 18 heap[0] = arr[0]; 19 for (int i = 1; i < arr.length; i++) { 20 int index = i; 21 for (int parent = getParent(index); parent >= 0; parent = getParent(parent)) { 22 switch (method) { 23 case ASC -> { 24 if (arr[i] > heap[parent]) { 25 heap[index] = heap[parent]; 26 index = parent; 27 } 28 } 29 case DESC -> { 30 if (arr[i] < heap[parent]) { 31 heap[index] = heap[parent]; 32 index = parent; 33 } 34 } 35 } 36 } 37 heap[index] = arr[i]; 38 } 39 return heap; 40 } 41 42 private static int getParent(int index) { 43 return index % 2 == 1 ? (index - 1) / 2 : index / 2 - 1; 44 } 45 46 @SuppressWarnings("preview") 47 private static void heap(int[] arr, int end, Method method) { 48 if (end > 2) { 49 int index = 0; 50 switch (method) { 51 case ASC -> { 52 index = arr[1] > arr[2] ? 1 : 2; 53 } 54 case DESC -> { 55 index = arr[1] < arr[2] ? 1 : 2; 56 } 57 } 58 swap(arr, index, end - 2); 59 swap(arr, 0, end - 1); 60 int[] heap = Arrays.copyOf(arr, end - 2); 61 adjust(heap, index, method); 62 adjust(heap, 0, method); 63 System.arraycopy(heap, 0, arr, 0, heap.length); 64 } else { 65 switch (method) { 66 case ASC -> { 67 if (arr[0] > arr[1]) swap(arr, 0, 1); 68 } 69 case DESC -> { 70 if (arr[0] < arr[1]) swap(arr, 0, 1); 71 } 72 } 73 } 74 } 75 76 @SuppressWarnings("preview") 77 private static void adjust(int[] heap, int index, Method method) { 78 switch (method) { 79 case ASC -> { 80 if (index < heap.length / 2) { 81 int lchild = index * 2 + 1; 82 int rchild = (index + 1) * 2; 83 if (rchild >= heap.length || heap[lchild] > heap[rchild]) { 84 if (heap[index] < heap[lchild]) { 85 swap(heap, index, lchild); 86 adjust(heap, lchild, method); 87 } 88 } else { 89 if (heap[index] < heap[rchild]) { 90 swap(heap, index, rchild); 91 adjust(heap, rchild, method); 92 } 93 } 94 } 95 } 96 case DESC -> { 97 if (index < heap.length / 2) { 98 int lchild = index * 2 + 1; 99 int rchild = (index + 1) * 2; 100 if (rchild >= heap.length || heap[lchild] < heap[rchild]) { 101 if (heap[index] > heap[lchild]) { 102 swap(heap, index, lchild); 103 adjust(heap, lchild, method); 104 } 105 } else { 106 if (heap[index] > heap[rchild]) { 107 swap(heap, index, rchild); 108 adjust(heap, rchild, method); 109 } 110 } 111 } 112 } 113 } 114 }
例如:对序列(5, 8, 3, 4, 9, 2, 7, 6, 1)进行升序排序。
生成大顶堆数组:
第一趟堆排序的过程为:
每一趟堆排序的结果为:
[5, 8, 3, 4, 9, 2, 7, 6, 1] --> [9, 8, 7, 6, 5, 2, 3, 4, 1] --> [7, 6, 3, 4, 5, 2, 1, 8, 9] -->
[5, 4, 3, 1, 2, 6, 7, 8, 9] --> [3, 1, 2, 4, 5, 6, 7, 8, 9] --> [1, 2, 3, 4, 5, 6, 7, 8, 9]