吃透排序
更新ing,有错误欢迎提出讨论~
一个网站
有个各类算法可视化的网站很不戳,对算法的执行过程不清晰地可以lou一眼,可以加深对算法的理解
网站地址:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
以排序算法为例,点开网站找到排序模块
点击任何一个排序进入,然后就可以愉快看到各种排序的动画
排序基本概念
思维导图
先上思维导图
概念理解
什么是算法的稳定性?
文绉绉地说就是排序后,能使关键字相同的元素保持原来顺序中的相对位置不变
如图就是相同的元素如两个3,排序后是否改变前后的相对位置
那稳定的排序算法一定优于不稳定的排序算法吗?
nonono,如果算法对稳定性压根没要求,或者根本没有相同的元素,那稳定的排序算法也派不上什么用场了
内部和外部排序有什么区别?
内部排序:数据全部在内存
外部排序:数据不全部在内存,有部分在外存
由于内存小,读写速度快,大约是外存(一般是硬盘)的600倍左右,所有内部排序我们更关注算法本身的优劣,即时间、空间复杂度
由于外存大,读写速度慢(数据取出,拍好序再存回外存都需要进行磁盘的读写),所以外部排序我们更关注磁盘的读写的次数,这个次数越少越好
插入排序
思维导图
拆解排序过程
插入排序又分为直接插入排序和折半插入排序
第一个元素默认排好,后面元素一个个按序插入
大致步骤就三步:
1.找
2.后移元素
3.插入
直接插入排序
上代码
//直接插入排序
void insertSort(int A[], int n) {
int i, j, temp;//存放待排序元素
for (i = 1; i < n; i++) {
//从第二个元素开始插入排序
if (A[i] < A[i - 1]) {
temp = A[i];
for (j = i - 1; j >= 0 && A[j] > temp; j--) {
//元素后移
A[j + 1] = A[j];
}
A[j + 1] = temp//将待排序元素插入到正确的位置
}
}
}
其实就是下图,需要j+1到正确的插入位置
还有一种带哨兵的直接插入排序的写法
什么是哨兵?
就是将数组A[0]的位置放放待排序的元素,然后整个数组的下标从1开始
上代码
// 带哨兵的直接插入排序
void insertSort1(int A[], int n) {
int i, j;
for (i = 2; i <= n; i++) {
if (A[i] < A[i - 1]) {
A[0] = A[i];
for (j = i - 1; A[0] < A[j]; j--) {
//元素后移
A[j + 1] = A[j];
}
A[j + 1] = A[0];//将待排序元素插入到正确的位置
}
}
}
上一种写法的temp相当于A[0]
虽然第二种后移元素的判断条件少了,算法效率有所提升,但是提升效果并不明显,所以两种写法二选一,选自己喜欢的
折半插入排序
在查找正确的插入位置时,有没有更高效的方法,答案是有的,就是利用折半查找。
how?
首先我们需要3个指针:low,mid,high
待排序元素(就是i指向的元素)与mid指向的元素进行比较
如果A[i]<=A[mid],则high=mid-1(说明元素要插入的位置在左半边)
如果A[i]>A[mid],则low=mid+1(说明元素要插入的位置在右半边)
查找停止条件是什么呢?
第一次比较之后low=mid+1,mid=(low+high)/2
再下一次55<70,所以high=mid-1,mid=(low+high)/2,变成下图
最后55<60,high=mid-1,
所以查找结束的条件是:low>high
okk,可以上代码了
// 带哨兵的折半插入排序
void insertSort2(int A[], int n) {
int i, j, low, high, mid;
for (i = 2; i <= n; i++) {
if (A[i] < A[i - 1]) {
A[0] = A[i];
low = 1;
high = i - 1;
while (low <= high) {
//折半查找插入位置
mid = (low + high) / 2;
if (A[mid] > A[0])
high = mid - 1;
else
low = mid + 1;
}
for (j = i - 1; j >= low; j--) {
//元素后移
A[j + 1] = A[j];
}
A[low] = A[0];//将待排序元素插入到正确的位置
}
}
}
性能分析
分析时间、空间复杂度
直接插入排序:只需要一个temp或者A[0]一个辅助空间,所以空间的复杂度是O(1)
最好时间复杂度:有序的情况,对于n-1个元素每次都只需要进行一次比较,一次插入,所以最好时间复杂度为O(n)
最坏时间复杂度:逆序的情况