排序算法总结-Shell排序、归并排序、快排、堆排

本章是对排序算法的一个总结,包括原理,代码,时间复杂度,空间复杂度等,总结内容不包括基础算法,如冒泡排序,插入排序,选择排序,另外文中代码均为手打,只展示核心思想,未经编译器测试。
对此约定一些基础函数:

  • void swap(num,a,b); //交换数组num中序号为a,b的数组元素
  • int max(a,b);//返回a,b中较大值。

Shell排序

shell排序基于插入排序,可以说是插入排序的改进版,大规模的排序数组会很麻烦,因为它只会交换旁边两个数字,所以数组只能一个一个的交换到远端去,所以我们希望能够让较大的数字能够提前到达后面去,降低后续排序交换次数。由此shell排序应运而生,其思想就是让任意相隔h的元素有序,h由大变小到为1,最后整个数组有序。

//num为排序数组,N为数组长度
void shellSort(int num[],int N){

int h=0;
while(h<N/3) h=h*3+1;  //1 4 13 40. ...这些数是由某些专家研究出来目前最高效的h
   while(h>=1){
for(int i=h;i<N;i++){
 //每次分成相隔h的一组元素,对其进行直接插入排序。
 // 例如当h为2,数组为1 2 3 4 5 6时,其分成了1 3 5,2 4 6这两组。
  forint j=i;j>=h;j=j-h) 
  {
               if(num[j]<num[j-h]) //移动位置
               swap(num,j,j-h);
               else //移动到了合适位置
               break;
   }
}
h=h/3;
}
}

希尔排序的性能目前无法准确的描述,影响其性能因素之一递增序列,即h,目前使用上述代码中的即可。
如无法理解需详解可移步shell排序详解

归并排序

归并排序利用了分治的思想,一组排序数中划分成n个数组,对每个子数组排序后,归并成一个更大的数组,解释下归并过程,假设数组a左右两半已经排序好了,将其复制到数组b,然后对照数组b将其一个个从大到小排入a中就行了。归并排序有两种实现思想,一种是自顶向下,一种是自底向上。

  • 自顶向下是利用递归,从最大分组开始,分成两半,左边递归排序好,右边递归排序好,最后合并。
  • 自底向上是遍历数组,从最开始的两个数为分组合并,层层往上,直到全部排序好。
    合并函数:
void merge(int low,int mid,int hi){
int i=low,j=mid+1;
 for(int i=0;i<=hi;i++)
    aux[i]=num[i]; //复制数组 
 for(int k=low;k<=hi;k++)
 {
  if(i>mid) a[k]=aux[j++]; //左边已经选完了
   else if(j>hi) a[k]=aux[i++]; //右边已经选完了
   else if(aux[i]>aux[j]) a[k]=aux[i++];
   else a[k]=aux[j++];
}
}

自顶向下归并排序

void sort(int low,int hi)
{
      if(hi<=low) return ;
      int mid=low+(hi-low)/2;
      sort(low,mid);//递归排序左边
      sort(mid+1,hi);//递归排序右边
      merge(low,mid,hi);
}

自底向上归并排序

 void sort(int N,int num[]){ //N为数组长度,num[]为数组
 for(int i=1;i<N;i=i+i;)//将其分割成i+i大小的子数组。
 for(int lo=0;lo<N-i;lo=lo+i+i) //对子数组左右进行归并
 merge(lo,lo+i-1,min(lo+i+i-1,N-1));
}

归并排序时间复杂度最小最坏都为NlogN,比较次数为1/2NlogN~NlogN,空间复杂度为N。
优化:对小规模数组使用插入排序

快速排序

快排是一种分治的排序算法,它与归并排序有些许类似,快排的排序方式是当两个子数组有序的时候整个数组也自然有序了,其数组切分位置取决于数组内容。一般将数组第一个数设为分割点,将大于此数的放右边,小于此数的放左边。再依次对左右两边递归使用,完成排序。


     void sort(int a[],int low,int hi)
    {
      if(hi<=low) return;
      int i=low,j=hi+1;
      int v=a[low];//以a[low]作为切分点
      while(i!=j){
    while(a[--j]>v&&i<j)
        if(i==hi) break; //从右边找到一个小于切分点的
    while(a[++i]<v&&i<j)
    if(j==low) break;//从左边找到一个大于切分点的
    if(i>=j) break;
    swap(a,i,j);//交换两数
    }
    swap(a,j,low); //最后将切分点与a[j]交换
    sort(a,low,j-1); //继续处理左边
    sort(a,j+1,hi);//继续处理右边

    }

快速排序平均复杂度NlogN,最坏和冒泡排序一样N平方。比较次数平均2NlnN~N2/2次比较。
改进:小数组用插入排序,三取样切分法。

堆排序

移步堆排序

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值