排序

1、直接插入排序:把后面未排序部分的首个数插入到前面已排序部分的正确位置上去,直到全部排好顺序。直接插入排序是稳定的,算法时间复杂度O(n^2)。
  1. /* 直接插入排序 */  
  2. void insert_sort(int v[],int n){  
  3.     register int i,j,temp;  
  4.     for(i=1; i<n; ++i){  /* 0~i-1为已排序部分,i~n-1为未排序部分 */  
  5.         temp=v[i];  /* 保存当前需要排序的元素 */  
  6.         for(j=i-1;j>=0 && v[j]>temp;--j) /* 在已排序部分中找出正确的插入位置 */  
  7.             v[j+1]=v[j];  /* 元素后移一位,最后j+1为空出的插入位置 */  
  8.         v[j+1]=temp; /* 插入元素 */  
  9.     }  
  10. }  

    2、shell排序:将要排序的一组数按某个增量g分成若干组,每组中记录的下标相差g。对每组中全部元素进行直接插入排序,然后缩小增量g,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。shell排序是不稳定的,算法时间复杂度可改进到O(n^1.25)左右。

  1. /* shell排序 */  
  2. void shell_sort(register int v[],int n){  
  3.     register int gap,i,j,temp;  
  4.     gap=1;  
  5.       
  6.     /* gap初始化为不大小n的序列(1、4、13、40、121、...)中的最小值 */  
  7.     do (gap=3*gap+1); while(gap<=n);  
  8.       
  9.     for(gap/=3;gap>0;gap/=3)   /* 缩小增量进采用除3的策略 */  
  10.         for(i=gap;i<n;++i){  /* 对当前组进行直接插入排序 */  
  11.             temp=v[i];  
  12.             /* 在当前组的已排序部分中找到正确的插入位置 */  
  13.             for(j=i-gap;(j>=0)&&(v[j]>temp);j-=gap)   
  14.                 v[j+gap]=v[j]; /* 元素后移,以空出插入位置 */  
  15.             v[j+gap]=temp;  /* 插入元素 */  
  16.         }  
  17. }  

    注意一般的shell排序实现都是gap初始化为n/2,并采用除2的缩小增量策略。这里初始化为不大小n的序列(1、4、13、40、121、...)中的最小值,并采用除以3的缩小增量策略,这可使排序加快20%~30%。内循环中的赋值从3次减少到1次。这里增加了register修饰符,使这些变量存储在寄存器中,在有些实现中,register声明可以大大提高性能(有的高达40%)。
    3、冒泡排序:对前面未排序部分每相邻的两个数进行比较和调整,让较大的数上浮到后面已排序部分的开头。冒泡排序是稳定的,算法时间复杂度O(n^2)。

  1. /* 冒泡排序 */  
  2. void bubble_sort(int b[],int n){  
  3.     register int i,j,temp,flag;  
  4.     for(i=n-1;i>=1;--i){ /* 0~i为未排序部分,i+1~n-1为已排序部分 */  
  5.         flag=0;  
  6.         for(j=0;j<i;++j)  /* 对未排序部分每相邻的两个数进行比较和调整 */  
  7.             if(b[j]>b[j+1]){ /* 大的上浮 */  
  8.                 temp=b[j];  
  9.                 b[j]=b[j+1];  
  10.                 b[j+1]=temp;  
  11.                 flag=1; /* 如果进行了交换,说明最初始的元素序列并不是有序的,flag设为1 */  
  12.             }  
  13.         if(flag==0) break/* 如果flag没有被设为1,说明第一轮扫描中没有作任何交换,这表明最初的元素序列 
  14.                                      是有序的,无需再排序了,直接break返回 */  
  15.     }  
  16. }  

    这里设置了一个flag,当元素序列本身就是有序时,直接返回,这可以加快排序过程。
    4、快速排序:扫描一次元素并不断地交换,使右边部分的各个数都比左边部分大。然后用同样的方法处理这两个子序列(用递归调用)。快速排序是不稳定的,最理想情况算法时间复杂度O(nlgn),最坏为O(n^2),空间复杂度需要O(lgn)。

  1. /* 快速排序 */  
  2. void quick_sort(int x[],int low,int high){  
  3.     register int i,j,temp;  
  4.     if (low < high){ /* 要排序的元素起止下标,保证小的放在左边,大的放在右边。这里以下标为low的元素为基准点 */  
  5.         i=low;  
  6.         j=high;  
  7.         temp=*(x+low); /* 暂存初始基准点的数 */  
  8.         while (i<j){ /*循环扫描*/  
  9.             while (i<j && *(x+j)>temp) /* 在右边的只要比基准点大仍放在右边 */  
  10.                 j--; /* 前移一个位置 */                 
  11.             if (i<j){  
  12.                 *(x+i)=*(x+j); /* 上面的循环退出:即出现比基准点小的数,替换基准点的数 */  
  13.                 i++; /* 基准点后移一个位置 */  
  14.             }  
  15.             while (i<j && *(x+i)<=temp) /* 在左边的只要小于等于基准点仍放在左边 */  
  16.                 i++; /* 后移一个位置 */  
  17.             if (i<j){  
  18.                 *(x+j)=*(x+i); /* 上面的循环退出:即出现比基准点大的数,放到右边 */  
  19.                 j--; /* 前移一个位置 */  
  20.             }  
  21.         }  
  22.         *(x+i)=temp; /* 一遍扫描完后,把初始基准点的值放到适当位置 */  
  23.         quick_sort(x,low,i-1);   /* 对基准点左边的数再执行快速排序 */  
  24.         quick_sort(x,i+1,high);  /* 对基准点右边的数再执行快速排序 */  
  25.     }  
  26. }  

    注意实现时要找一个基准点的数,扫描完一遍后,左边部分都会小于基准点数,右边部分都会大于基准点数。因此开始时要暂存基准点数,扫描完一遍后把基准点插入到中间恰当的位置,这样就分出了左边部分和右边部分,然后递归调用即可。
    5、选择排序:在后面未排序的部分中选择最小的数插入到前面已排序部分的末尾。直到倒数第二个数和最后一个数比较为止。选择排序是不稳定的,算法复杂度O(n^2)。

  1. /* 选择排序 */  
  2. void select_sort(int x[], int n){  
  3.     register int i,j,min,temp;  
  4.     for(i=0; i<n-1; ++i){  /* 0~i为已排序部分,i+1~n-1为未排序部分 */  
  5.         min=i;  /* 假设当前下标为i的数最小,比较后再调整 */  
  6.         for(j=i+1; j<n; ++j)  /* 找出最小的数的下标 */  
  7.             if(*(x+j) < *(x+min))  
  8.                 min=j;  
  9.         if (min != i){   /* 如果min在循环中改变了,就需要交换数据 */  
  10.             temp=*(x+i);  
  11.             *(x+i)=*(x+min); /* 把最小数交换到已排序部分的末尾 */  
  12.             *(x+min)=temp;  
  13.         }  
  14.     }  
  15. }  

    6、堆排序:是对选择排序的改进。把数组看作是二叉树,b[k]的两个子树为元素x[2*k]与x[2*k+1]。堆是一棵树,每个节点的数值不小于其后代节点的数值。初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储顺序,使之成为一个堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面n-1个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。堆排序是不稳定的,算法时间复杂度O(nlgn)。

  1. /* 堆排序 */  
  2. /* 调整成为堆 */  
  3. void adjust(int x[], int n, int s){  
  4.     register int temp,k,j;  
  5.     temp=*(x+s); /* 暂存开始元素 */  
  6.     k=s;  /* 开始元素下标 */  
  7.     j=2*k+1; /* 右子树元素下标 */  
  8.     while (j<n){  
  9.         if (j<n-1 && *(x+j) < *(x+j+1)) /* 判断是否满足堆的条件:满足就继续下一轮比较,否则调整 */  
  10.             j++;  
  11.         if (temp<*(x+j)){ /* 找到更大的数:调整上来 */  
  12.             *(x+k)=*(x+j);  
  13.             k=j; /* 调整后,开始元素也随之调整 */  
  14.             j=2*k+1;  
  15.         }else       
  16.             break/* 没有需要调整了,已经是个堆了,退出循环 */  
  17.     }  
  18.     *(x+k)=temp; /* 开始元素放到它正确位置 */  
  19. }  
  20. /* 排序函数 */  
  21. void heap_sort(int x[], int n){  
  22.     register int i,k,temp;  
  23.     for (i=n/2-1; i>=0; i--) /* 扫描整个数组来建立一个初始堆 */  
  24.         adjust(x,n,i);   
  25.            
  26.     for (k=n-1; k>=1; k--){  
  27.         temp=*(x+0); /* 堆顶放到最后 */  
  28.         *(x+0)=*(x+k);  
  29.         *(x+k)=temp;  
  30.         adjust(x,k,0); /* 剩下的数再调整为堆 */   
  31.     }  
  32. }  

    注意堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成,一是建堆的调整函数,二是反复调用实现排序的函数。
    7、归并排序:设两个有序的子序列(相当于输入堆)放在同一向量中相邻的位置上R[low..m],R[m+1..high],先将它们合并到一个局部的暂存向量R1(相当于输出堆)中,待合并完成后将R1复制回R[low..high]中。归并排序是稳定的,算法时间复杂度为O(nlgn),空间复杂度需要O(n)。

  1. /* 归并排序 */  
  2. /* 合并两个有序子序列  */  
  3. void merge(int array[],int flow,int middle,int high){  
  4.     /* flow~middle和middle+1~high为数组array的两个有序子序列 */  
  5.     register int i,j,k;  
  6.     int n1=middle-flow+1,n2=high-middle;  
  7.     int temp1[n1]; int temp2[n2];  
  8.     for (i=0; i<n1; ++i) /* 把子序列flow~middle拷贝到temp1中 */  
  9.         temp1[i]=array[flow+i];  
  10.     for (i=0; i<n2; ++i) /* 把子序列iddle+1~high拷贝到temp2中 */  
  11.         temp2[i]=array[middle+i+1];  
  12.           
  13.     for (j=0,k=0,i=flow; i<=high; ++i){  
  14.         if (j<n1&&k<n2){   /* 从大到小的顺序把两个子序列写入到array中 */  
  15.             if (temp1[j]>=temp2[k])  
  16.                 array[i]=temp2[k++];  
  17.             else  
  18.                 array[i]=temp1[j++];  
  19.         }  
  20.         else/* 一个子序列写入完后,另一个子序列可能还有剩余元素,把剩余元素也写入到array中 */  
  21.             if(j>=n1&&k<n2)  
  22.                 array[i]=temp2[k++];  
  23.             if(j<n1&&k>=n2)  
  24.                 array[i]=temp1[j++];  
  25.         }  
  26.     }  
  27. }  
  28. /* 排序函数 */  
  29. void merge_sort(int array[],int s,int t){  
  30.     register int m;  
  31.     if (s<t) {  
  32.         m=(s+t)/2; /* 将数组分成两个子序列 */  
  33.         merge_sort(array,s,m);  /* 对左边子序列递归使用归并排序 */  
  34.         merge_sort(array,m+1,t); /* 对右边子序列也递归使用归并排序 */  
  35.         merge(array,s,m,t); /* 合并排好序的两个子序列 */  
  36.     }  
  37. }  

    8、测试代码。

  1. #include <stdio.h>  
  2. #define MAX 8  
  3. /* 这里放各个排序算法的实现函数 */  
  4. /* 输入要排序的测试数据 */  
  5. void input(int a[],int k){  
  6.     register int i;  
  7.     printf("Input %d number for sorting: /n",k);  
  8.     for( i=0;i<k;++i)  
  9.         scanf("%d",a++);  
  10.     printf("/n");  
  11. }  
  12. /* 打印数组中的各个元素 */  
  13. void print(int b[],int k){   
  14.     int i;  
  15.     for(i=0;i<k;++i)  
  16.         printf("%d ",b[i]);  /* 一个元素占4个字符的宽度 */  
  17.     printf("/n");  
  18. }  
  19. int main(){   
  20.     int *p,a[MAX];  
  21.    
  22.     /* 输入测试数据 */  
  23.     p=a;  
  24.     input(p,MAX);  
  25.     /* 测试直接插入排序 */   
  26.     /* 
  27.     p=a; 
  28.     insert_sort(p,MAX); 
  29.     print(a,MAX);  
  30.     */  
  31.       
  32.     /* 测试shell排序 */   
  33.     /* 
  34.     p=a; 
  35.     shell_sort(p,MAX); 
  36.     print(a,MAX);  
  37.     */  
  38.     /* 测试冒泡排序 */   
  39.     /* 
  40.     p=a; 
  41.     insert_sort(p,MAX); 
  42.     print(a,MAX);   
  43.     */  
  44.     /* 测试快速排序 */   
  45.     /* 
  46.     p=a; 
  47.     quick_sort(p,0,MAX-1); 
  48.     print(a,MAX);  
  49.     */  
  50.     /* 测试选择排序 */   
  51.     /* 
  52.     p=a; 
  53.     select_sort(a,10); 
  54.     print(a,MAX);  
  55.     */  
  56.     /* 测试堆排序 */   
  57.     /* 
  58.     p=a; 
  59.     heap_sort(p,MAX); 
  60.     print(a,MAX);  
  61.     */  
  62.       
  63.     /* 测试归并排序 */   
  64.     p=a;  
  65.     merge_sort(p,0,MAX-1);  
  66.     print(a,MAX);  
  67.     return 0;  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值