《数据结构与算法之美》笔记 — 排序 (上)
2020-01-09 本文已影响0人
波波维奇c
个人博客首发:https://wubobo952.github.io/post/sorts/
如何分析排序算法
- 最好情况,最坏情况,平均情况的时间复杂度:
分析时,最好要结合需要排序的原始数据,进行三种时间复杂度的情况分析。 - 时间复杂度的系数,常数,低阶:
时间复杂度在数据规模很大的情况下,会忽略系数,常数,低阶。但是在实际的开发中,规模很小的情况下,在对同一阶的排序算法性能比较的时候,就要把系数,常数,低阶考虑进来。 - 比较次数和交换(或移动)次数:
排序算法在排序过程中,会涉及到比较大小和移动或交换元素,所以,如果在分析排序算法执行效率的时候,应该要把移动或交换次数考虑进去。 - 排序算法的内存消耗:
算法的内存消耗,可以通过空间复杂度来衡量,针对排序算法的空间复杂度的一个新概念 — 原地排序 ,是指空间复杂度是 O(1) 的排序算法。 - 排序算法的稳定性:
是指,带排序的序列中如果存在值相等的元素,经过排序后,相等的元素之间的先后顺序不变。
常见的排序算法
冒泡排序:
菜鸟教程图片冒泡排序是比较相邻的两个元素,如果满足交换条件就进行元素交换,每比较一次,至少让一个元素移动到它应该在的位置,那么移动 n 次就是完成了 n 个数据的排序。
- 时间复杂度度计算:排序一次,需要对比元素就少一个,那么就是 (n-1)+(n-2)+(n-3)+1
根据等差数列求和 得到 n(n-1)/2 根据大0公式 时间复杂度为 n^2 - 原地排序算法:冒泡排序只涉及相邻的数据的交换,只需要常量级的临时空间,空间复杂度为 O(1) ,是一个原地排序算法。
- 稳定的排序算法:冒泡排序中,只有交换才会改变元素的前后顺序,当相邻的两个元素相等时,是不做交换的.
- 最好情况下,要排序的序列已经排好序了,只进行一次就行,O(1), 最坏的情况下就是倒序排好的序列,需要进行 n 次冒泡, O(n^2)
- 平均时间复杂度O(n^2)
/**
* 冒泡排序
*/
private static int[] sort(int[] n) {
for (int i = 0; i < n.length; i++) {
for (int j = 0; j < n.length - i - 1; j++) {
if (n[j] > n[j + 1]) {
int temp = n[j];
n[j] = n[j + 1];
n[j + 1] = temp;
}
}
}
return n;
}
插入排序
菜鸟教程图片- 插入排序的核心把序列分为已排序,未排序。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序的元素在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束
- 原地排序,稳定的排序算法
/**
* 插入排序:分 已排序部分,非排序部分
*/
private static int[] insertSort(int[] n) {
for (int i = 1; i < n.length; i++) {
int temp = n[i];
int j = i - 1;
for (; j >= 0; j--) {
if (n[j] > temp) {
n[j + 1] = n[j];
} else {
break;
}
}
n[j + 1] = temp;
}
return n;
}
选择排序
菜鸟教程图片- 选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾
- 原地排序算法
- 不稳定的排序算法:因为选择排序每次都要找剩余未排序元素中的最小值,并和前面的元素交换位置,这样破坏了稳定性
private static int[] selectSort(int[] n) {
for (int i = 0; i < n.length; i++) {
int minIndex = i;
for (int j = i + 1; j <= n.length - 1; j++) {
if (n[j] < n[minIndex]) {
minIndex = j;
}
}
if (i != minIndex) {
int temp = n[i];
n[i] = n[minIndex];
n[minIndex] = temp;
}
}
return n;
}
虽然 冒泡排序 和 插入排序 时间复杂度是一样的,但是 相同数据规模的情况下,插入排序耗费的时间更短,因为在冒泡排序中赋值的操作有3次,而插入只有1次。