考纲
(只考虑内部排序)
1插入排序(直插(稳),希尔)
2交换排序(冒泡(稳),快排)
3选择排序(简选,堆排)
4归并排序(稳)
5基数排序(稳)
6算法笔记
对任意n个关键字排序的比较次数至少为log₂(n!)
1.插入排序
1.1.直接插入排序(稳定)
适合顺序表,也可适用于链表,所以两个都需实现
(用折半查找优化时,只能用顺序表!!!)
1.查找插入位置(外部大循环)//第一个元素总是有序的,从第二个开始比较
2.后移(内部小循环)
void InsertSort(ElemType A[],int n){ //顺序表实现直接插入排序
//1.第一步找插入位置
for(int i=2;i<=n;i++){ //从第二个元素比较之前有序的序列,找到
if(A[i]<A[i-1]){ //如果if一直不成立,则空循环n趟就完了,若if成立(存在一个数小于其前驱),则进行插入操作
A[0]=A[i]; //哨兵暂存“待插入点”
for(int j=i-1;A[0]<A[j];--j){ //在待插点之前的有序序列中查找位置
A[j+1]=A[j]; //不断后移
}
A[j+1]=A[0]; //最后的位置为j+1,因为是--j,最后一次循环的时候减了1
}// (if)
} //(for)
}
折半优化查找可减少比较次数!!但不减少移动次数
void InsertSort(ElemType A[],int n){ //顺序表实现直接插入排序
//1.第一步找插入位置
for(int i=2;i<=n;i++){ //从第二个元素比较之前有序的序列,找到
if(A[i]<A[i-1]){ //如果if一直不成立,则空循环n趟就完了,若if成立(存在一个数小于其前驱),则进行插入操作
(这个位置的代码改变,外面的大循环是不变的)
1.由顺序查变为折半查,2.边查边后移变为,先查到再统一后移。
}// (if)
//为啥改进过的直插,还是O(n²)?这里看到两个for循环,外层大循环,套这里的移动小循环。
for(j=i-1;j>=high+1;--j){ //找到位置了 就是 low或 high+1;
A[j+1]=A[j]; //统一后移
}
A[high+1]=A[0]; //或 A[low]=A[0]
} //(for)
}
具体代码
int low,high,mid;
A[0]=A[i];
low=1;high=i-1; //折半的范围是 从第一个到第i-1个(有序的序列)
while(low<=high){ //这个while+if 是由那个for转化来的,
mid=(low+high)/2;
if(A[mid]>A[0])
high=mid-1;
else
low=high+1;
}
单链表实现
//需要声明三个指针,p(工作指针,大循环), pre(工作指针,小循环), r(暂存p的后继,防止断链)
void InsertSort(LinkList &L){
LNode *p=L->next;
LNode *pre,*r=p->next; //这里的r存p->next,即初始时的第二个元素,p一会就把表搞断,先存着
p->next=null; //为什么p后继赋值null?表示只有一个数据,肯定有序