文章目录
最经典的、最常用的:冒泡排序、插入排序、选择排序、归并排序、快速排序、计数排序、基数排序、桶排序。
思考题:插入排序和冒泡排序的时间复杂度相同,都是O(n2),在实际的软件开发里,为什么我们更倾向于使用插入排序算法而不是冒泡排序算法呢?
1. 如何分析⼀个“排序算法”?
1.1 排序算法的执⾏效率
从以下几方面考虑:
- 最好情况、最坏情况、平均情况时间复杂度。考虑算法在不同数据下的性能表现;
- 时间复杂度的系数、常数 、低阶。时间复杂度是反应n规模很大时候的趋势,忽略系数、常数 、低阶,但是实际软件开发,排序数据是样本量有限的数据,必须考虑系数、常数 、低阶;
- ⽐较次数和交换(或移动)次数。
1.2 排序算法的内存消耗
原地排序(Sorted in place):就是特指空间复杂度是O(1)的排序算法。
1.3 排序算法的稳定性
稳定排序算法:数据中相同的值经过排序后,相对位置不变。
应用价值:订单有两个属性,⼀个是下单时间,另⼀个是订单⾦额。如果我们现在
有10万条订单数据,我们希望按照⾦额从⼩到⼤对订单数据排序。对于⾦额相同的订单,我们希望按照下单时间从早到晚有序。
解决方案:应用稳定的排序算法,先根据下单时间排序,然后根据订单金额排序,因为是稳定算法,所以对于金额相同的订单,下单时间还是有序的。
2.冒泡排序(Bubble Sort)
// 插入排序,a表示数组,n表示数组大小
public void insertionSort(int[] a, int n) {
if (n <= 1) return;
for (int i = 1; i < n; ++i) {
int value = a[i];
int j = i - 1;
// 查找插入的位置
for (; j >= 0; --j) {
if (a[j] > value) {
a[j+1] = a[j]; // 数据移动
} else {
break;
}
}
a[j+1] = value; // 插入数据
}
}
冒泡是原地排序,稳定排序。
最好情况时间复杂度是O(n)。以最坏情况时间复杂度为O(n )。
平均复杂度怎么分析?
-
有序度是数组中具有有序关系的元素对的个数。
对于⼀个完全有序的数组,⽐如1,2,3,4,5,6,有序度就是n*(n-1)/2,也就是15。我们把这种完全有序的数组的有序度叫作满有序度。 -
逆序度,与有序度相反
-
逆序度=满有序度-有序度
冒泡排序包含两个操作原⼦,⽐较和交换。每交换⼀次,有序度就加1。不管算法怎么改进,交换次数总是确定的,即为逆序度,也就是n*(n-1)/2–初始有序度。此例中就是15–3=12,要进⾏12次交换操作。
对于包含n个数据的数组进⾏冒泡排序,平均交换次数是多少呢?最坏情况下,初始状态的有序度是0,所以要进⾏n*(n-1)/2次交换。最好情况下,初始状态的有序度是n*(n-1)/2,就不需要进⾏交换。我们可以取个中间值n*(n-1)/4,来表示初始有序度既不是很⾼也不是很低的平均情况。
换句话说,平均情况下,需要n*(n-1)/4次交换操作,比较操作肯定要比交换操作多,而复杂度的上限是O(n2),所以平均情况下的时间复杂度就是O(n2)。
3.插⼊排序(Insertion Sort)
public static void insertionSort2(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 1; i < arr.length; i++) {
int temp = arr[i];
int j = i - 1;
for (; j >= 0; j--) {
if (arr[j] > temp) {
// 数据移动
arr[j + 1] = arr[j];
} else {
break;
}
}
// 数据交换
arr[j + 1] = temp;
}
}
插入排序是原地,稳定的排序算法。
最好的时间复杂度为O(n),最坏情况时间复杂度为O(n2)。
4.选择排序(Selection Sort)
选择排序是原地,非稳定的排序算法。
最好最坏时间复杂度为O(n^2)。
5. 解答为什么插入排序比冒泡排序更受欢迎
两个都是原地,稳定的排序,时间复杂度都是O(n^2), 移动次数都是原始数据的逆序度。
为什么插入排序更好呢?
冒泡排序中数据的交换操作:
if (a[j] > a[j+1]) { // 交换
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
flag = true;
}
插入排序中数据的移动操作:
if (a[j] > value) {
a[j+1] = a[j]; // 数据移动
} else {
break;
}
逆序度是K的数组,冒泡是3k单位时间,插入是1k单位时间,显然插入更好。