总结
排序算法 | 时间复杂度 | 空间复杂度 | 是否稳定 | 主体思路 |
选择排序 | O(n^2) | O(1) | 不稳定 | 在第i步找到最小的值,放在i位置 |
插入排序 | O(n^2) | O(1) | 稳定 | 每次循环从0开始把更大的值向右移动,只跟自己前一个值比较 |
冒泡排序 | O(n^2) | O(1) | 稳定 | 每次循环从0开始把更大的值向右移动,只跟自己前一个值比较 |
快速排序 | O(n*log n) | O(log n) | 不稳定 | 先取第一个数作为基准,先把数组中更大的数放在右边,更小的数放在左边,然后再分别对左右两个子数组递归处理。 之所以更快是因为利用了之前计算的结果,在后面处理的过程中范围更小了。 |
归并排序 | O(n*log n) | O(n) | 稳定 | 先把数组一分为二,先把2个子数组分别排序,然后在把两个子数组合并起来。 其中要用到递归。 这里更快主要是在2个子数组合并的时候,充分利用之前已经比较得到的顺序 |
堆排序 | O(n*log n) | O(1) | 不稳定 | |
桶排序 | O(n+N) | O(n+N) | 不稳定 |
注意:这里的稳定性,不是说算法的效率是否稳定的。而是说:在排序前后,值相同的2个元素,相对顺序是否一致。比如,{1,3,2,4,2} 这里有两个2,在排序后,前面的那个2是不是还在排在前面,如果是,则说这个排序算法是稳定的;如果不是或者不确定,则说是不稳定的。
算法稳定性在对于基础类型的数据排序,基本上没有什么作用,但对于复杂对象的就比较有用。比如一个Student(int age, int class)数组,如果算法是稳定的,我们先按班级排序一次,再按年龄排序一次,就可以得到按年龄、班级排序的结果。
一、选择排序
/**
* 选择排序的核心是:在第i步找到最小的值,放在i位置
* @param nums
*/
public static void selectionSort(int[] nums) {
for (int i = 0; i < nums.length; i ++) {
int minIndex = i;
for (int j = i + 1; j < nums.length; j ++) {
if (nums[j] < nums[minIndex]) {
minIndex = j;
}
}
swap(nums, i, minIndex);
}
}
二、插入排序
/**
* 插入排序的核心是:每次循环从0开始把更大的值向右移动,只跟自己前一个值比较
* @param nums
*/
public static void insertSort(int[] nums) {
for (int i = 1; i < nums.length; i ++) {
for (int j = i; j > 0; j --) {
// 如果前一个数比当前数更大,进行换位
if (nums[j] < nums[j - 1]) {
swap(nums, j, j - 1);
}
}
}
}
三、冒泡排序
/**
* 冒泡排序的核心是:每次循环从0开始把更大的值向右移动,只跟自己前一个值比较
* @param nums
*/
public static void bubbleSort(int[] nums) {
for (int i = nums.length - 1; i >= 0; i --) {
for (int j = 1; j <= i; j ++) {
// 如果前一个数比当前数更大,进行换位
if (nums[j] < nums[j - 1]) {
swap(nums, j, j - 1);
}
}
}
}
四、快速排序
/**
* 快速排序的核心是:先取第一个数作为基准,先把数组中更大的数放在右边,更小的数放在左边,然后再分别对左右两个子数组递归处理。
* 之所以更快是因为利用了之前计算的结果,在后面处理的过程中范围更小了。
* @param nums
*/
public static void quickSort(int[] nums) {
quickSortRange(nums, 0, nums.length - 1);
}
public static void quickSortRange(int[] nums, int left, int right) {
if (right <= 0 || left >= nums.length - 1 || left >= right) {
return;
}
int i = left, j = right;
int base = nums[left];
while (i != j) {
while (nums[j] >= base && j > i) {
j --;
}
while (nums[i] <= base && j > i) {
i++;
}
if (j > i) {
swap(nums, i, j);
}
}
swap(nums, left, i);
quickSortRange(nums, left, i - 1);
quickSortRange(nums, i + 1, right);
}
五、归并排序
/**
* 归并排序的核心是:先把数组一分为二,先把2个子数组分别排序,然后在把两个子数组合并起来。
* 其中要用到递归
* @param nums
*/
public static void mergeSort(int[] nums) {
// 这里需要额外的数组,额外空间复杂度n
int[] newNums = new int[nums.length];
mergeSort(nums, 0, nums.length - 1, newNums);
}
public static void mergeSort(int[] nums, int left, int right, int[] newNums) {
if (left >= right) {
return;
}
int middle = left + (right - left) / 2;
mergeSort(nums, left, middle, newNums);
mergeSort(nums, middle + 1, right, newNums);
merge(nums, left, middle, right, newNums);
}
private static void merge(int[] nums, int left, int middle, int right, int[] newNums) {
int index1 = left;
int index2 = middle + 1;
int newIndex = left;
// 比较大小,顺序插入到
while (index1 <= middle && index2 <= right) {
if (nums[index1] > nums[index2]) {
newNums[newIndex ++] = nums[index2 ++];
} else {
newNums[newIndex ++] = nums[index1 ++];
}
}
while (index1 <= middle) {
newNums[newIndex ++] = nums[index1 ++];
}
while (index2 <= right) {
newNums[newIndex ++] = nums[index2 ++];
}
for (int i = left; i <= right; i ++) {
nums[i] = newNums[i];
}
}
六、堆排序
待实现
七、桶排序
待实现