排序算法

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. /*函数声明部分*/  
  4. void insert_sort(int par_array[], int array_size);  
  5. void print_array(int par_array[], int array_size);  
  6. void shell_sort(int par_array[], int array_size);  
  7. /*函数定义部分*/  
  8. void print_array(int par_array[], int array_size)  
  9. {  
  10.     int i;  
  11.     for(i = 0; i < array_size; ++i)  
  12.         printf("%d/t", par_array[i]);  
  13.     printf("/n");  
  14. }  
  15. /* 
  16.  * 插入排序 
  17.  * 采用改进的插入排序算法 
  18.  * 将待插入的记录a[i]的关键字从右到左一次与有序区中的记录a[j](j=i-1,i-2,...1)的关键字比较: 
  19.  * 1.如果a[j]的关键字大于a[i]的关键字,则将a[j]后移一位 
  20.  * 2.如果a[j]的关键字小于或等于a[i]的关键字,则查找过程结束,(j+1)即为a[i]的插入位置 
  21.  */  
  22. void insert_sort(int par_array[], int array_size)  
  23. {  
  24.     int i,j;  
  25.     int temp;  
  26.     for(i = 1; i < array_size; i++)  
  27.     {  
  28.         temp = par_array[i];  
  29.         for(j = i-1; j >= 0; j--)  
  30.         {  
  31.             if(temp < par_array[j])  
  32.             {  
  33.                 par_array[j+1] = par_array[j];  
  34.             }  
  35.             else  
  36.                 break;  
  37.         }  
  38.         par_array[j+1] = temp;  
  39.     }  
  40. }  
  41. /* 
  42.  * 希尔排序 
  43.  * 改进的插入排序算法 
  44.  * 算法先将要排序的一组数按某个增量d分成若干组,对每个组中全部元素进行排序, 
  45.  * 然后用一个较小的增量对它进行再次分组,并对每个新组进行排序, 
  46.  * 当增量减到1时,整个要排序的数被分成一组,排序完成。 
  47.  */  
  48. void shell_sort(int par_array[], int array_size)  
  49. {  
  50.     int i,j;  
  51.     int h;  
  52.     int temp;  
  53.     for(h = array_size/2; h > 0; h=h/2)/*控制增量*/  
  54.     {  
  55.         for(i = h; i < array_size; i++)  
  56.         {  
  57.             temp = par_array[i];  
  58.             for(j = i-h; j >= 0; j-=h)  
  59.             {  
  60.                 if(temp < par_array[j])  
  61.                 {  
  62.                     par_array[j+h] = par_array[j];  
  63.                 }  
  64.                 else  
  65.                     break;  
  66.             }  
  67.             par_array[j+h] = temp;  
  68.         }  
  69.     }  
  70. }  
  71. /*测试用例*/  
  72. int main()  
  73. {  
  74.     int i_array[] = {7,3,5,8,9,1,2,4,6};  
  75.     int array_size = sizeof(i_array)/sizeof(int);  
  76.     int i_array2[] = {7,3,5,8,9,1,2,4,6};  
  77.     int array_size2 = sizeof(i_array2)/sizeof(int);  
  78.       
  79.     printf("Before insert_sort:/n");  
  80.     print_array(i_array, array_size);  
  81.     insert_sort(i_array, array_size);  
  82.     printf("After insert_sort/n");  
  83.     print_array(i_array, array_size);  
  84.   
  85.     printf("Before shell_sort:/n");  
  86.     print_array(i_array2, array_size2);  
  87.     shell_sort(i_array2, array_size2);  
  88.     printf("After shell_sort/n");  
  89.     print_array(i_array2, array_size2);  
  90.     system("pause");  
  91. }  
  92. /* 
  93. Before insert_sort: 
  94. 7       3       5       8       9       1       2       4       6 
  95. After insert_sort 
  96. 1       2       3       4       5       6       7       8       9 
  97. Before shell_sort: 
  98. 7       3       5       8       9       1       2       4       6 
  99. After shell_sort 
  100. 1       2       3       4       5       6       7       8       9 
  101. 请按任意键继续. . . 
  102.  */  

(1)“冒泡法” 

冒泡法大家都较熟悉。其原理为从a[0]开始,依次将其和后面的元素比较,若a[0]>a,则交换它们,一直比较到a[n]。同理对a[1],a[2],...a[n-1]处理,即完成排序。下面列出其代码:  
C/C++ code
            
            
void bubble( int * a, int n) /* 定义两个参数:数组首地址与数组大小 */ { int i,j,temp; for (i = 0 ;i < n - 1 ;i ++ ) for (j = i + 1 ;j < n;j ++ ) /* 注意循环的上下限 */ if (a[i] > a[j]) { temp = a[i]; a[i] = a[j]; a[j] = temp; } }

冒泡法原理简单,但其缺点是交换次数多,效率低。  
下面介绍一种源自冒泡法但更有效率的方法“选择法”。  

2)“选择法” 

选择法循环过程与冒泡法一致,它还定义了记号k=i,然后依次把a[k]同后面元素比较,若a[k]>a[j],则使k=j.最后看看k=i是否还成立,不成立则交换a[k],a,这样就比冒泡法省下许多无用的交换,提高了效率。  
C/C++ code
            
            
void choise( int * a, int n) { int i,j,k,temp; for (i = 0 ;i < n - 1 ;i ++ ) { k = i; /* 给记号赋值 */ for (j = i + 1 ;j < n;j ++ ) if (a[k] > a[j]) k = j; /* 是k总是指向最小元素 */ if (i != k) { /* 当k!=i是才交换,否则a即为最小 */ temp = a; a = a[k]; a[k] = temp; } } }

选择法比冒泡法效率更高,但说到高效率,非“快速法”莫属,现在就让我们来了解它。  

(3)“快速法”
  
快速法定义了三个参数,(数组首地址*a,要排序数组起始元素下标i,要排序数组结束元素下标j). 它首先选一个数组元素(一般为a[(i+j)/2],即中间元素)作为参照,把比它小的元素放到它的左边,比它大的放在右边。然后运用递归,在将它左,右两个子数组排序,最后完成整个数组的排序。下面分析其代码:  
C/C++ code
            
            
void quick( int * a, int i, int j) { int m,n,temp; int k; m = i; n = j; k = a[(i + j) / 2 ]; /* 选取的参照 */ do { while (a -< k && m < j) m ++ ; /* 从左到右找比k大的元素 */ while (a[ n] > k && n > i) n -- ; /* 从右到左找比k小的元素 */ if (m <= n) { /* 若找到且满足条件,则交换 */ temp = a - ; a -= a[n]; a[n] = temp; m ++ ; n -- ; } } while (m <= n); if (m < j) quick(a,m,j); /* 运用递归 */ if (n > i) quick(a,i,n); }


(4)“插入法” 

插入法是一种比较直观的排序方法。它首先把数组头两个元素排好序,再依次把后面的元素插入适当的位置。把数组元素插完也就完成了排序。  
C/C++ code
            
            
void insert( int * a, int n) { int i,j,temp; for (i = 1 ;i < n;i ++ ) { temp = a[i]; /* temp为要插入的元素 */ j = i - 1 ; while (j >= 0 && temp < a[j]) { /* 从a开始找比a小的数,同时把数组元素向后移 */ a[j + 1 ] = a[j]; j -- ; } a[j + 1 ] = temp; /* 插入 */ } }


5)“shell法” 

shell法是一个叫 shell 的美国人与1969年发明的。它首先把相距k(k>=1)的那几个元素排好序,再缩小k值(一般取其一半),再排序,直到k=1时完成排序。下面让我们来分析其代码:
C/C++ code
            
            
void shell( int * a, int n) { int i,j,k,x; k = n / 2 ; /* 间距值 */ while (k >= 1 ) { for (i = k;i < n;i ++ ) { x = a; j = i - k; while (j >= 0 && x < a[j]) { a[j + k] = a[j]; j -= k; } a[j + k] = x; } k /= 2 ; /* 缩小间距值 */ } }

上面我们已经对几种排序法作了介绍,现在让我们写个主函数检验一下。
C/C++ code
            
            
#include < stdio.h > /* 别偷懒,下面的"..."代表函数体,自己加上去哦! */ void bubble( int * a, int n) { ... } void choise( int * a, int n) { ... } void quick( int * a, int i, int j) { ... } void insert( int * a, int n) { ... } void shell( int * a, int n) { ... } /* 为了打印方便,我们写一个print吧。 */ void print( int * a, int n) { int i; for (i = 0 ;i < n;i ++ ) printf( " %5d " ,a); printf( " \n " ); } main() { /* 为了公平,我们给每个函数定义一个相同数组 */ int a1[] = { 13 , 0 , 5 , 8 , 1 , 7 , 21 , 50 , 9 , 2 }; int a2[] = { 13 , 0 , 5 , 8 , 1 , 7 , 21 , 50 , 9 , 2 }; int a3[] = { 13 , 0 , 5 , 8 , 1 , 7 , 21 , 50 , 9 , 2 }; int a4[] = { 13 , 0 , 5 , 8 , 1 , 7 , 21 , 50 , 9 , 2 }; int a5[] = { 13 , 0 , 5 , 8 , 1 , 7 , 21 , 50 , 9 , 2 }; printf( " the original list: " ); print(a1, 10 ); printf( " according to bubble: " ); bubble(a1, 10 ); print(a1, 10 ); printf( " according to choise: " ); choise(a2, 10 ); print(a2, 10 ); printf( " according to quick: " ); quick(a3, 0 , 9 ); print(a3, 10 ); printf( " according to insert: " ); insert(a4, 10 ); print(a4, 10 ); printf( " according to shell: " ); shell(a5, 10 ); print(a5, 10 ); }


6)希尔排序

算法思想简单描述:

在直接插入排序算法中,每次插入一个数,使有序序列只增加1个节点,
并且对插入下一个数没有提供任何帮助。如果比较相隔较远距离(称为
增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除
多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现
了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中
记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量
对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成
一组,排序完成。
C/C++ code
            
            
void shell_sort( int * x, int n) { int h, j, k, t; for (h = n / 2 ; h > 0 ; h = h / 2 ) /* 控制增量 */ { for (j = h; j < n; j ++ ) /* 这个实际上就是上面的直接插入排序 */ { t = * (x + j); for (k = j - h; (k >= 0 && t <* (x + k)); k -= h) { * (x + k + h) = * (x + k); } * (x + k + h) = t; } } }


几种排序算法的比较和选择
1. 选取排序方法需要考虑的因素:
(1) 待排序的元素数目n;
(2) 元素本身信息量的大小;
(3) 关键字的结构及其分布情况;
(4) 语言工具的条件,辅助空间的大小等。
小结:
(1) 若n较小(n <= 50),则可以采用直接插入排序或直接选择排序。由于直接插入排序所需的记录移动操作较直接选择排序多,因而当记录本身信息量较大时,用直接选择排序较好。
(2) 若文件的初始状态已按关键字基本有序,则选用直接插入或冒泡排序为宜。
(3) 若n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序。快速排序是目前基于比较的内部排序法中被认为是最好的方法。
(4) 在基于比较排序方法中,每次比较两个关键字的大小之后,仅仅出现两种可能的转移,因此可以用一棵二叉树来描述比较判定过程,由此可以证明:当文件的n个关键字随机分布时,任何借助于"比较"的排序算法,至少需要O(nlog2n)的时间。
(5) 当记录本身信息量较大时,为避免耗费大量时间移动记录,可以用链表作为存储结构。





  • #18楼 得分:1回复于:2010-04-04 13:59:41
    顶一下,lz功德~
    另外补充一点点:
    基于交换的排序的核心是一次相邻的交换操作只能消除一个逆序(inversion)。而可以证明一个N个元素的序列中的逆序数平均为 N(N-1)/4,所以基于相邻的交换的排序法的时间复杂度都是O(n^2),要打破就要采用远距离交换,比如 shell 法。
    堆排序的算法时间复杂度稳定,选择它就不怕worst-case;
    Shell法如果选取比较好的增量序列(比如sedgewick序列),速度上可以超过堆排序,但它不是稳定的排序算法。
    归并排序一般不用于内部排序(空间占用大)。
    快排有待研究,要特别注意正确的写法(特别是指针转换)
     
 
#28楼 得分:0回复于:2010-04-06 18:12:39
C/C++ code
            
            
// 可以设置一个变量来提高效率 // 如果在一次中没有执行交换操作,说明已经有序了,不用再接着向下比较了。 void bubble( int * a, int n) /* 定义两个参数:数组首地址与数组大小 */ { int i,j,temp; int exchange; for (i = 0 ;i < n - 1 ;i ++ ) { exchange = 0 ; for (j = 0 ;j < n - 1 ;j ++ ) /* 注意循环的上下限 */ { if (a[j] > a[j + 1 ]) { temp = a[j]; a[j] = a[j + 1 ]; a[j + 1 ] = temp; exchange = 1 ; } } if ( ! exchange) break ; else contiune; } }
 




#46楼 得分:0回复于:2010-10-01 14:17:48
C/C++ code
            
            
// 堆排序(从下标为1的位置开始) void heap_adjust( int * p, int root, int len) { int i = 2 * root; int finish = 0 ; int t = p[root]; while (i <= len && finish == 0 ) { if (i < len) { if (p[i] < p[i + 1 ]) i ++ ; } if (t >= p[i]) { finish = 1 ; } else { p[i / 2 ] = p[i]; i = 2 * i; } } p[i / 2 ] = t; } void heapSort( int * p, int len) { int i; for (i = len / 2 ; i > 0 ; i -- ) { heap_adjust(p, i, len); } for (i = len; i >= 1 ; i -- ) { p[ 0 ] = p[ 1 ]; p[ 1 ] = p[i]; p[i] = p[ 0 ]; heap_adjust(p, 1 , i - 1 ); } }

C/C++ code
            
            
// 基数排序 int temp_digit( int temp, int flag) { int i; i = temp / ( int )pow( 10 ,flag) % 10 ; return i; } void baseSort( int * p, int len) { int i,j,k,index,temp; int count[ 10 ]; int array[ 10 ][ 20 ] = { 0 }; for (i = 0 ; i < 3 ; i ++ ) { for (j = 0 ; j < 10 ; j ++ ) { count[j] = 0 ; } for (j = 0 ; j < len; j ++ ) { temp = temp_digit(p[j],i); array[temp][count[temp] ++ ] = p[j]; } index = 0 ; for (j = 0 ; j < len; j ++ ) { for (k = 0 ; k < count[j]; k ++ ) { p[index] = array[j][k]; index ++ ; } } } }
 
 
 




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值