直接插入排序
基本思想:假设有n个元素,当前考虑到第i个元素,前i-1个元素已经排好顺序,将第i个元素插入到前i-1个合适的位置。
步骤:
1、设置一个“哨兵”把第i个元素放到"哨兵"上,比较的时候直接拿"哨兵"比较即可。
2、从i-1往前查找合适的位置,并把比当前大的元素往后挪。
void insert_sort(int n){
int j;
for(int i=2;i<=n;i++){
if(arr[i]<arr[i-1]){//如果arr[i]>=arr[i-1]的话,那就没必要对元素i排序了。
arr[0] = arr[i];//“哨兵”
for(j = i-1;arr[0]<arr[j];j--){//从后往前查找
arr[j+1] = arr[j];//第j个元素比arr[0]大,挪到第j+1位置上,继续往前查。
}
//走到这一步说明arr[j]>=arr[0]了,所以arr[0]放到arr[j]的后边就可以了。
arr[j+1] = arr[0];
}
}
}
算法分析:
空间复杂度:O(1)
时间复杂度:O(n^2)
最好情况下:数组本身有序,不需要插入,比较n-1次就可以了。
最坏情况下:数组本身逆序,第i(i>=2)个元素需要 比较i次,挪动i+1次。
算法特点:稳定,可用于链式和顺序,当初始无序且n较大时,不适用。
折半插入排序
基本思想:在原来直接插入排序的基础上,通过二分来确定第i个元素的合适的位置,这个位置就是从左到右首个严格大于arr[0]的元素(或者说是最后一个小于等于arr[i]的后边那个)然后这个位置就是元素arr[i]的合适位置(设为mid),然后将(mid–i-1)区间元素后移动一下。在把arr[i]放到arr[mid]上。
void insert_sort(int n){
int l,r,high,low,mid;
for(int i=2;i<=n;i++){
if(arr[i]<arr[i-1]){
arr[0] = arr[i];
low = 1,high = i-1;
while(low<=high){//二分找出第大于arr[0]的位置(或者是最后一个小于等于arr[0]的位置)
mid = (low+high)>>1;
if(arr[mid]>arr[0]) {
// ans = mid;//从左到右边第一个大于arr[0]的数的下标,那high一定是从左到
// 右最后一个小于等于arr[0]的数的下标。那么arr[0]应该放在high+1的处。
high = mid-1;
}
else low = mid+1;
}
for(int j = i-1;j>=high+1;j--){
arr[j+1] = arr[j];
}
arr[high+1] = arr[0];
}
}
}
算法分析:
时间复杂度:O(n^2)
空间复杂度:O(1)
插入第i个元素时,需要经过log(i)+1次比较。
算法特点:稳定排序,只能用与顺序表,适合初识记录无序,n较大的情况。
希尔排序
基本思想:又叫做“缩小增量排序”,对数组按照一个逐级递减的步长分组,对每组进行直接插入排序,让数组大致有序,当步长减为1时,就是一个普通的直接插入排序。
void insert_sort(int n){
int j;
for(int dk = n/2;dk>=1;dk/=2){//步长
for(int i=dk+1;i<=n;i++){
// 先对第一组的元素排序,在对第二组,第三组....第一组,第二组....
if(arr[i]<arr[i-dk]){//当前组元素arr[i]的前一个元素为arr[i-dk]
arr[0] = arr[i];
for(j = i-dk;j>=0&&arr[j]>arr[0];j-=dk){
//加上j>=0是必要的,因为这j有可能取负值,当j取负值的时候说明arr[0]在当前组是最小的,只需要插入到当前组的第一个即可
arr[j+dk] = arr[j];
}
arr[j+dk] = arr[0];
}
}
}
}
算法分析:
空间复杂度:O(1)
时间复杂度:O(n^2)(有争议)
特点:不稳定,用于顺序不可用于链式,增量(步长)序列的取法无1之外的公因子,并且最后一个增量值为1。适合初始无序,n较大的情况。