希尔排序
这里内部用了直接插入的方法;
- 将待排序的序列按某种规则分成几个子序列(这个规则就是增量,增量一般选序列长度的一半,然后逐半递减,直到最后增量为1,为1时就相当于直接插入)
- 分别对几个子序列进行直接插入排序
- 就是在直接插入的基础上增加了跨度(增量),跨度逐渐减半,当跨度为1时已经完成排序
- 每次隔跨度直接插入法排序完成,表示记录隔跨度有序,当跨度不断缩小,成为1时也就表示相邻之间有序,也就是已经排序完成
1 void HashSort(int a[],int n){ 2 int i,j,temp; 3 int gap=n; 4 do{ 5 gap=gap/3+1; 6 for(i=gap;i<n;i++){ 7 if(a[i]<a[i-gap]){ 8 temp=a[i]; 9 //注:这里要手动检测越界问题 10 for(j=i-gap;a[j]>temp && j>=0;j-=gap) 11 a[j+gap]=a[j]; 12 a[j+gap]=temp; 13 } 14 } 15 }while(gap>1); 16 }
堆排序
对选择排序的改进,每次找最小值的时候没有利用上次的结果,所以将记录全部存入完全二叉树中,构造大顶堆或小顶堆,每次取一个极值,这样就可以利用前一次的比较结果了
- 将数据存入二叉堆(完全二叉树:大顶堆/小顶堆)
- 大顶堆根结点赋值到尾部记录(小顶堆赋头部)【大顶堆:根结点大于两子结点】
- 然后将剩余的 n-1 从新构造堆
- 重复以上两步,便堆序列进行了排序
完全二叉树的特点:
- 根结点为n,左孩子为2*n,右孩子2*n+1
- 完全二叉树中有n个结点时,树中有子子树的结点数目为【n/2】,且【n/2】为最后一个有子树的结点,
其实也不需要构造二叉树,需要将数组放弃0下标位置用其他下标对应相应完全二叉树结点即可
1 //i:规范位置;n:堆的结点树 2 void HeapAdjust(int a[],int i,int n){ 3 int k=a[i],j=i; 4 for(j*=2;j<n;j*=2){ 5 //找到子结点中较大的那个 6 if(j+1<n && a[j+1]>a[j]) 7 j++; 8 //较大的子结点小于根节点就退出 9 if(k>=a[j]) 10 break; 11 //较大的子结点覆盖双亲结点 12 //覆盖后,较大子结点这个位置就是新的规范位置 13 a[i]=a[j]; 14 i=j; 15 } 16 //退出循环后就说明,位置是已经规范,直接将规范值赋到该处即可 17 a[i]=k; 18 } 19 20 void HeapSort(int a[],int n){ 21 int i,k; 22 //构建大顶堆 23 for(i=n/2;i>0;i--){ 24 HeapAdjust(a,i,n); 25 } 26 //循环将顶部最大值与尾部交换 27 for(;n>1;n--){ 28 k=a[1]; 29 a[1]=a[n-1]; 30 a[n-1]=k; 31 //交换后恢复大顶堆 32 HeapAdjust(a,1,n-1); 33 } 34 }
快速排序
冒泡排序有相似的原理
- 先从序列中选一记录作为标准数,并将序列中所有比该记录小的记录放在左边,大的放在右边(结束之后这就是他排序之后应该位置)
- 再分别对两边进行递归(直到待处理的序列长度为一,处理结束)
1 int partition(int a[],int low,int high){ 2 //令0下标位置记录为标准数 3 int sign=a[low]; 4 while(low<high){ 5 //注:下面两循环中若与标志为相等也交换的话,那么将死循环,这个与标志数相等的数将和标准数互相一直换下去 6 //比标准数大的放左边 7 //因为上面的标志数直接取了low位置,所以下面直接覆盖即可,不需要交换,退出循环后直接将标志数赋在应有位置 8 while(low<high && a[high]>=sign) 9 high--; 10 a[low]=a[high]; 11 //比标准数小的放右边 12 while(low<high && a[low]<=sign) 13 low++; 14 a[high]=a[low]; 15 } 16 //退出循环之后low=high,且这个位置存放的就是标准数 17 a[low]=sign; 18 return low; 19 } 20 21 void QSort(int a[],int low,int high){ 22 int sign; 23 if(low<high){ 24 sign=partition(a,low,high); 25 QSort(a,low,sign-1); 26 QSort(a,sign+1,high); 27 } 28 } 29 30 void QuickSort(int a[],int n){ 31 QSort(a,0,n-1); 32 }
其实快速排序法适用于规模较大的排序,而直接插入法则对规模较小的排序效率较高(长度分界为7),所以在快速排序中对长度小于7的排序段用直接插入法就可以进一步优化快速排序
1 void QSort(int a[],int low,int high){ 2 int sign; 3 if((high-low)>7){ 4 sign=partition(a,low,high); 5 QSort(a,low,sign-1); 6 QSort(a,sign+1,high); 7 }else{ 8 InsertSort(k+low,high-low+1); 9 } 10 }
当然,快速排序法中有两次递归调用,也是可以利用伪递归的方法对其进行更深一步的优化