前言
本文介绍几种经典、常考的内排序算法。
本文试图建立一个比较标准的快速排序、归并排序、堆排序算法代码。
选择排序
了解。
算法思想
每一轮选取未排定的部分中最小的部分交换到未排定部分的最开头,经过若干个步骤,就能排定整个数组。即:先选出最小的,再选出第 2 小的,以此类推。
实现
略
插入排序
了解
算法思想
每次将一个数字插入一个有序的数组里,成为一个长度更长的有序数组,有限次操作以后,数组整体有序。
实现
略
冒泡排序
了解
算法思想
外层循环每一次经过两两比较,把每一轮未排定部分最大的元素放到了数组的末尾;
「冒泡排序」有个特点:在遍历的过程中,提前检测到数组是有序的,从而结束排序,而不像「选择排序」那样,即使输入数据是有序的,「选择排序」依然需要「傻乎乎」地走完所有的流程。
实现
略
希尔排序
了解
算法思想
插入排序的优化。在插入排序里,如果靠后的数字较小,它来到前面就得交换多次。「希尔排序」改进了这种做法。带间隔地使用插入排序,直到最后「间隔」为 11 的时候,就是标准的「插入排序」,此时数组里的元素已经「几乎有序」了。
实现
略
快速排序
重要
算法思想
快速排序每一次都排定一个元素(这个元素呆在了它最终应该呆的位置),然后递归地去排它左边的部分和右边的部分,依次进行下去,直到数组有序。
实现(递归)
class Quick {
public static void sort(int[] nums) { shuffle(nums); sort(nums, 0, nums.length - 1); }
private static void sort(int[] nums, int lo, int hi) { if (lo >= hi) { return; } int p = partition(nums, lo, hi);
sort(nums, lo, p - 1); sort(nums, p + 1, hi); }
private static int partition(int[] nums, int lo, int hi) { int pivot = nums[lo]; int i = lo + 1, j = hi; while (i <= j) { while (i < hi && nums[i] <= pivot) { i++; } while (j > lo && nums[j] > pivot) { j--; }
if (i >= j) { break; } swap(nums, i, j); } swap(nums, lo, j); return j; }
private static void shuffle(int[] nums) { Random rand = new Random(); int n = nums.length; for (int i = 0 ; i < n; i++) { int r = i + rand.nextInt(n - i); swap(nums, i, r); } }
private static void swap(int[] nums, int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; } }
|
实现(非递归)
暂略
归并排序
重要
算法思想
借助额外空间,合并两个有序数组,得到更长的有序数组。
实现
class Merge {
private static int[] temp;
public static void sort(int[] nums) { temp = new int[nums.length]; sort(nums, 0, nums.length - 1); }
private static void sort(int[] nums, int lo, int hi) { if (lo == hi) { return; } int mid = lo + (hi - lo) / 2; sort(nums, lo, mid); sort(nums, mid + 1, hi); merge(nums, lo, mid, hi); }
private static void merge(int[] nums, int lo, int mid, int hi) { for (int i = lo; i <= hi; i++) { temp[i] = nums[i]; }
int i = lo, j = mid + 1; for (int p = lo; p <= hi; p++) { if (i == mid + 1) { nums[p] = temp[j++]; } else if (j == hi + 1) { nums[p] = temp[i++]; } else if (temp[i] > temp[j]) { nums[p] = temp[j++]; } else { nums[p] = temp[i++]; } } } }
|
堆排序
重要
算法思想
将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列。
实现
public class Solution {
public int[] sortArray(int[] nums) { int len = nums.length; heapify(nums);
for (int i = len - 1; i >= 1; ) { swap(nums, 0, i); i--; siftDown(nums, 0, i); } return nums; }
private void heapify(int[] nums) { int len = nums.length; for (int i = (len - 1) / 2; i >= 0; i--) { siftDown(nums, i, len - 1); } }
private void siftDown(int[] nums, int k, int end) { while (2 * k + 1 <= end) { int j = 2 * k + 1; if (j + 1 <= end && nums[j + 1] > nums[j]) { j++; } if (nums[j] > nums[k]) { swap(nums, j, k); } else { break; } k = j; } }
private void swap(int[] nums, int index1, int index2) { int temp = nums[index1]; nums[index1] = nums[index2]; nums[index2] = temp; } }
|
排序复杂度比较
参考资料
- 复习基础排序算法
- 菜鸟教程:十大排序算法
- 《算法 第四版》
- 快速排序的正确理解方式及运用
- 归并排序的正确理解方式及运用