码迷,mamicode.com
首页 > 编程语言 > 详细

八大排序算法

时间:2016-08-23 18:34:52      阅读:366      评论:0      收藏:0      [点我收藏+]

标签:

 
 

概述

排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。

我们这里说说八大排序就是内部排序。

技术分享

    

    当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。

   快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;


 

1.插入排序—直接插入排序(Straight Insertion Sort)

基本思想:

将一个记录插入到已排序好的有序表中,从而得到一个新,记录数增1的有序表。即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。

要点:设立哨兵,作为临时存储和判断数组边界之用。

直接插入排序示例:

技术分享

 

如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。

算法的实现:

[cpp] view plain copy
 
 print?
  1. void print(int a[], int n ,int i){  
  2.     cout<<i <<":";  
  3.     for(int j= 0; j<8; j++){  
  4.         cout<<a[j] <<" ";  
  5.     }  
  6.     cout<<endl;  
  7. }  
  8.   
  9.   
  10. void InsertSort(int a[], int n)  
  11. {  
  12.     for(int i= 1; i<n; i++){  
  13.         if(a[i] < a[i-1]){               //若第i个元素大于i-1元素,直接插入。小于的话,移动有序表后插入  
  14.             int j= i-1;   
  15.             int x = a[i];        //复制为哨兵,即存储待排序元素  
  16.             a[i] = a[i-1];           //先后移一个元素  
  17.             while(x < a[j]){  //查找在有序表的插入位置  
  18.                 a[j+1] = a[j];  
  19.                 j--;         //元素后移  
  20.             }  
  21.             a[j+1] = x;      //插入到正确位置  
  22.         }  
  23.         print(a,n,i);           //打印每趟排序的结果  
  24.     }  
  25.       
  26. }  
  27.   
  28. int main(){  
  29.     int a[8] = {3,1,5,7,2,4,9,6};  
  30.     InsertSort(a,8);  
  31.     print(a,8,8);  
  32. }  

效率:

时间复杂度:O(n^2).

其他的插入排序有二分插入排序,2-路插入排序。

 

 2. 插入排序—希尔排序(Shell`s Sort)

希尔排序是1959 年由D.L.Shell 提出来的,相对直接排序有较大的改进。希尔排序又叫缩小增量排序

基本思想:

先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

操作方法:

  1. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  2. 按增量序列个数k,对序列进行k 趟排序;
  3. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

希尔排序的示例:

技术分享

 

算法实现:

 

我们简单处理增量序列:增量序列d = {n/2 ,n/4, n/8 .....1} n为要排序数的个数

即:先将要排序的一组记录按某个增量dn/2,n为要排序数的个数)分成若干组子序列,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。继续不断缩小增量直至为1,最后使用直接插入排序完成排序。

[cpp] view plain copy
 
 print?
  1. void print(int a[], int n ,int i){  
  2.     cout<<i <<":";  
  3.     for(int j= 0; j<8; j++){  
  4.         cout<<a[j] <<" ";  
  5.     }  
  6.     cout<<endl;  
  7. }  
  8. /** 
  9.  * 直接插入排序的一般形式 
  10.  * 
  11.  * @param int dk 缩小增量,如果是直接插入排序,dk=1 
  12.  * 
  13.  */  
  14.   
  15. void ShellInsertSort(int a[], int n, int dk)  
  16. {  
  17.     for(int i= dk; i<n; ++i){  
  18.         if(a[i] < a[i-dk]){          //若第i个元素大于i-1元素,直接插入。小于的话,移动有序表后插入  
  19.             int j = i-dk;     
  20.             int x = a[i];           //复制为哨兵,即存储待排序元素  
  21.             a[i] = a[i-dk];         //首先后移一个元素  
  22.             while(x < a[j]){     //查找在有序表的插入位置  
  23.                 a[j+dk] = a[j];  
  24.                 j -= dk;             //元素后移  
  25.             }  
  26.             a[j+dk] = x;            //插入到正确位置  
  27.         }  
  28.         print(a, n,i );  
  29.     }  
  30.       
  31. }  
  32.   
  33. /** 
  34.  * 先按增量d(n/2,n为要排序数的个数进行希尔排序 
  35.  * 
  36.  */  
  37. void shellSort(int a[], int n){  
  38.   
  39.     int dk = n/2;  
  40.     while( dk >= 1  ){  
  41.         ShellInsertSort(a, n, dk);  
  42.         dk = dk/2;  
  43.     }  
  44. }  
  45. int main(){  
  46.     int a[8] = {3,1,5,7,2,4,9,6};  
  47.     //ShellInsertSort(a,8,1); //直接插入排序  
  48.     shellSort(a,8);           //希尔插入排序  
  49.     print(a,8,8);  
  50. }  

 

希尔排序时效分析很难,关键码的比较次数与记录移动次数依赖于增量因子序列d的选取,特定情况下可以准确估算出关键码的比较次数和记录的移动次数。目前还没有人给出选取最好的增量因子序列的方法。增量因子序列可以有各种取法,有取奇数的,也有取质数的,但需要注意:增量因子中除1 外没有公因子,且最后一个增量因子必须为1。希尔排序方法是一个不稳定的排序方法。

 

3. 选择排序—简单选择排序(Simple Selection Sort)

基本思想:

在要排序的一组数中,选出最小(或者最大)的个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后个数)比较为止。

简单选择排序的示例:

 技术分享

操作方法:

第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换;

第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换;

以此类推.....

第i 趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换,

直到整个序列按关键码有序。


算法实现:

[cpp] view plain copy
 
 print?
  1. void print(int a[], int n ,int i){  
  2.     cout<<"第"<<i+1 <<"趟 : ";  
  3.     for(int j= 0; j<8; j++){  
  4.         cout<<a[j] <<"  ";  
  5.     }  
  6.     cout<<endl;  
  7. }  
  8. /** 
  9.  * 数组的最小值 
  10.  * 
  11.  * @return int 数组的键值 
  12.  */  
  13. int SelectMinKey(int a[], int n, int i)  
  14. {  
  15.     int k = i;  
  16.     for(int j=i+1 ;j< n; ++j) {  
  17.         if(a[k] > a[j]) k = j;  
  18.     }  
  19.     return k;  
  20. }  
  21.   
  22. /** 
  23.  * 选择排序 
  24.  * 
  25.  */  
  26. void selectSort(int a[], int n){  
  27.     int key, tmp;  
  28.     for(int i = 0; i< n; ++i) {  
  29.         key = SelectMinKey(a, n,i);           //选择最小的元素  
  30.         if(key != i){  
  31.             tmp = a[i];  a[i] = a[key]; a[key] = tmp; //最小元素与第i位置元素互换  
  32.         }  
  33.         print(a,  n , i);  
  34.     }  
  35. }  
  36. int main(){  
  37.     int a[8] = {3,1,5,7,2,4,9,6};  
  38.     cout<<"初始值:";  
  39.     for(int j= 0; j<8; j++){  
  40.         cout<<a[j] <<"  ";  
  41.     }  
  42.     cout<<endl<<endl;  
  43.     selectSort(a, 8);  
  44.     print(a,8,8);  
  45. }  

 简单选择排序的改进——二元选择排序

简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。具体实现如下:

[cpp] view plain copy
 
 print?
  1. void SelectSort(int r[],int n) {  
  2.     int i ,j , min ,max, tmp;  
  3.     for (i=1 ;i <= n/2;i++) {    
  4.         // 做不超过n/2趟选择排序   
  5.         min = i; max = i ; //分别记录最大和最小关键字记录位置  
  6.         for (j= i+1; j<= n-i; j++) {  
  7.             if (r[j] > r[max]) {   
  8.                 max = j ; continue ;   
  9.             }    
  10.             if (r[j]< r[min]) {   
  11.                 min = j ;   
  12.             }     
  13.       }    
  14.       //该交换操作还可分情况讨论以提高效率  
  15.       tmp = r[i-1]; r[i-1] = r[min]; r[min] = tmp;  
  16.       tmp = r[n-i]; r[n-i] = r[max]; r[max] = tmp;   
  17.   
  18.     }   
  19. }  

4. 选择排序—堆排序(Heap Sort)

堆排序是一种树形选择排序,是对直接选择排序的有效改进。

基本思想:

堆的定义如下:具有n个元素的序列(k1,k2,...,kn),当且仅当满足

技术分享

时称之为堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最小项(小顶堆)。
若以一维数组存储一个堆,则堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的。如:

(a)大顶堆序列:(96, 83,27,38,11,09)

  (b)  小顶堆序列:(12,36,24,85,47,30,53,91)

技术分享

 

初始时把要排序的n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n 个元素中最小(或最大)的元素,这时堆的根节点的数最小(或者最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n 个元素中次小(或次大)的元素。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。称这个过程为堆排序

因此,实现堆排序需解决两个问题:
1. 如何将n 个待排序的数建成堆;
2. 输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆。


首先讨论第二个问题:输出堆顶元素后,对剩余n-1元素重新建成堆的调整过程。
调整小顶堆的方法:

1)设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素。将堆底元素送入堆顶((最后一个元素与堆顶进行交换),堆被破坏,其原因仅是根结点不满足堆的性质。

2)将根结点与左、右子树中较小元素的进行交换。

3)若与左子树交换:如果左子树堆被破坏,即左子树的根结点不满足堆的性质,则重复方法 (2).

4)若与右子树交换,如果右子树堆被破坏,即右子树的根结点不满足堆的性质。则重复方法 (2).

5)继续对不满足堆性质的子树进行上述交换操作,直到叶子结点,堆被建成。

称这个自根结点到叶子结点的调整过程为筛选。如图:

技术分享


再讨论对n 个元素初始建堆的过程。
建堆方法:对初始序列建堆的过程,就是一个反复进行筛选的过程。

1)n 个结点的完全二叉树,则最后一个结点是第技术分享个结点的子树。

2)筛选从第技术分享个结点为根的子树开始,该子树成为堆。

3)之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。

如图建堆初始过程:无序序列:(49,38,65,97,76,13,27,49)
                              技术分享


                              技术分享

 

 算法的实现:

从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。

[cpp] view plain copy
 
 print?
  1. void print(int a[], int n){  
  2.     for(int j= 0; j<n; j++){  
  3.         cout<<a[j] <<"  ";  
  4.     }  
  5.     cout<<endl;  
  6. }  
  7.   
  8.   
  9.   
  10. /** 
  11.  * 已知H[s…m]除了H[s] 外均满足堆的定义 
  12.  * 调整H[s],使其成为大顶堆.即将对第s个结点为根的子树筛选,  
  13.  * 
  14.  * @param H是待调整的堆数组 
  15.  * @param s是待调整的数组元素的位置 
  16.  * @param length是数组的长度 
  17.  * 
  18.  */  
  19. void HeapAdjust(int H[],int s, int length)  
  20. {  
  21.     int tmp  = H[s];  
  22.     int child = 2*s+1; //左孩子结点的位置。(i+1 为当前调整结点的右孩子结点的位置)  
  23.     while (child < length) {  
  24.         if(child+1 <length && H[child]<H[child+1]) { // 如果右孩子大于左孩子(找到比当前待调整结点大的孩子结点)  
  25.             ++child ;  
  26.         }  
  27.         if(H[s]<H[child]) {  // 如果较大的子结点大于父结点  
  28.             H[s] = H[child]; // 那么把较大的子结点往上移动,替换它的父结点  
  29.             s = child;       // 重新设置s ,即待调整的下一个结点的位置  
  30.             child = 2*s+1;  
  31.         }  else {            // 如果当前待调整结点大于它的左右孩子,则不需要调整,直接退出  
  32.              break;  
  33.         }  
  34.         H[s] = tmp;         // 当前待调整的结点放到比其大的孩子结点位置上  
  35.     }  
  36.     print(H,length);  
  37. }  
  38.   
  39.   
  40. /** 
  41.  * 初始堆进行调整 
  42.  * 将H[0..length-1]建成堆 
  43.  * 调整完之后第一个元素是序列的最小的元素 
  44.  */  
  45. void BuildingHeap(int H[], int length)  
  46. {   
  47.     //最后一个有孩子的节点的位置 i=  (length -1) / 2  
  48.     for (int i = (length -1) / 2 ; i >= 0; --i)  
  49.         HeapAdjust(H,i,length);  
  50. }  
  51. /** 
  52.  * 堆排序算法 
  53.  */  
  54. void HeapSort(int H[],int length)  
  55. {  
  56.     //初始堆  
  57.     BuildingHeap(H, length);  
  58.     //从最后一个元素开始对序列进行调整  
  59.     for (int i = length - 1; i > 0; --i)  
  60.     {  
  61.         //交换堆顶元素H[0]和堆中最后一个元素  
  62.         int temp = H[i]; H[i] = H[0]; H[0] = temp;  
  63.         //每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整  
  64.         HeapAdjust(H,0,i);  
  65.   }  
  66. }   
  67.   
  68. int main(){  
  69.     int H[10] = {3,1,5,7,2,4,9,6,10,8};  
  70.     cout<<"初始值:";  
  71.     print(H,10);  
  72.     HeapSort(H,10);  
  73.     //selectSort(a, 8);  
  74.     cout<<"结果:";  
  75.     print(H,10);  
  76.   
  77. }  


分析:

设树深度为k,技术分享。从根到叶的筛选,元素比较次数至多2(k-1)次,交换记录至多k 次。所以,在建好堆后,排序过程中的筛选次数不超过下式: 

                                技术分享

而建堆时的比较次数不超过4n 次,因此堆排序最坏情况下,时间复杂度也为:O(nlogn )。

 

5. 交换排序—冒泡排序(Bubble Sort)

基本思想:

在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。

冒泡排序的示例:

 技术分享

算法的实现:

 

[cpp] view plain copy
 
 print?
  1. void bubbleSort(int a[], int n){  
  2.     for(int i =0 ; i< n-1; ++i) {  
  3.         for(int j = 0; j < n-i-1; ++j) {  
  4.             if(a[j] > a[j+1])  
  5.             {  
  6.                 int tmp = a[j] ; a[j] = a[j+1] ;  a[j+1] = tmp;  
  7.             }  
  8.         }  
  9.     }  
  10. }  


冒泡排序算法的改进

对冒泡排序常见的改进方法是加入一标志性变量exchange,用于标志某一趟排序过程中是否有数据交换,如果进行某一趟排序时并没有进行数据交换,则说明数据已经按要求排列好,可立即结束排序,避免不必要的比较过程。本文再提供以下两种改进算法:

1.设置一标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。

改进后算法如下:

[cpp] view plain copy
 
 print?
  1. void Bubble_1 ( int r[], int n) {  
  2.     int i= n -1;  //初始时,最后位置保持不变  
  3.     while ( i> 0) {   
  4.         int pos= 0; //每趟开始时,无记录交换  
  5.         for (int j= 0; j< i; j++)  
  6.             if (r[j]> r[j+1]) {  
  7.                 pos= j; //记录交换的位置   
  8.                 int tmp = r[j]; r[j]=r[j+1];r[j+1]=tmp;  
  9.             }   
  10.         i= pos; //为下一趟排序作准备  
  11.      }   
  12. }    

2.传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值,我们考虑利用在每趟排序中进行正向和反向两遍冒泡的方法一次可以得到两个最终值(最大者和最小者) , 从而使排序趟数几乎减少了一半。

改进后的算法实现为:

[cpp] view plain copy
 
 print?
  1. void Bubble_2 ( int r[], int n){  
  2.     int low = 0;   
  3.     int high= n -1; //设置变量的初始值  
  4.     int tmp,j;  
  5.     while (low < high) {  
  6.         for (j= low; j< high; ++j) //正向冒泡,找到最大者  
  7.             if (r[j]> r[j+1]) {  
  8.                 tmp = r[j]; r[j]=r[j+1];r[j+1]=tmp;  
  9.             }   
  10.         --high;                 //修改high值, 前移一位  
  11.         for ( j=high; j>low; --j) //反向冒泡,找到最小者  
  12.             if (r[j]<r[j-1]) {  
  13.                 tmp = r[j]; r[j]=r[j-1];r[j-1]=tmp;  
  14.             }  
  15.         ++low;                  //修改low值,后移一位  
  16.     }   
  17. }   

6. 交换排序—快速排序(Quick Sort)

基本思想:

1)选择一个基准元素,通常选择第一个元素或者最后一个元素,

2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。

3)此时基准元素在其排好序后的正确位置

4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。

快速排序的示例:

(a)一趟排序的过程:

技术分享

(b)排序的全过程

技术分享

算法的实现:

 递归实现:

[cpp] view plain copy
 
 print?
  1. void print(int a[], int n){  
  2.     for(int j= 0; j<n; j++){  
  3.         cout<<a[j] <<"  ";  
  4.     }  
  5.     cout<<endl;  
  6. }  
  7.   
  8. void swap(int *a, int *b)  
  9. {  
  10.     int tmp = *a;  
  11.     *a = *b;  
  12.     *b = tmp;  
  13. }  
  14.   
  15. int partition(int a[], int low, int high)  
  16. {  
  17.     int privotKey = a[low];                             //基准元素  
  18.     while(low < high){                                   //从表的两端交替地向中间扫描  
  19.         while(low < high  && a[high] >= privotKey) --high;  //从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端  
  20.         swap(&a[low], &a[high]);  
  21.         while(low < high  && a[low] <= privotKey ) ++low;  
  22.         swap(&a[low], &a[high]);  
  23.     }  
  24.     print(a,10);  
  25.     return low;  
  26. }  
  27.   
  28.   
  29. void quickSort(int a[], int low, int high){  
  30.     if(low < high){  
  31.         int privotLoc = partition(a,  low,  high);  //将表一分为二  
  32.         quickSort(a,  low,  privotLoc -1);          //递归对低子表递归排序  
  33.         quickSort(a,   privotLoc + 1, high);        //递归对高子表递归排序  
  34.     }  
  35. }  
  36.   
  37. int main(){  
  38.     int a[10] = {3,1,5,7,2,4,9,6,10,8};  
  39.     cout<<"初始值:";  
  40.     print(a,10);  
  41.     quickSort(a,0,9);  
  42.     cout<<"结果:";  
  43.     print(a,10);  
  44.   
  45. }  


分析:

快速排序是通常被认为在同数量级(O(nlog2n))的排序方法中平均性能最好的。但若初始序列按关键码有序或基本有序时,快排序反而蜕化为冒泡排序。为改进之,通常以“三者取中法”来选取基准记录,即将排序区间的两个端点与中点三个记录关键码居中的调整为支点记录。快速排序是一个不稳定的排序方法。

 
快速排序的改进

在本改进算法中,只对长度大于k的子序列递归调用快速排序,让原序列基本有序,然后再对整个基本有序序列用插入排序算法排序。实践证明,改进后的算法时间复杂度有所降低,且当k取值为 8 左右时,改进算法的性能最佳。算法思想如下:

[cpp] view plain copy
 
 print?
  1. void print(int a[], int n){  
  2.     for(int j= 0; j<n; j++){  
  3.         cout<<a[j] <<"  ";  
  4.     }  
  5.     cout<<endl;  
  6. }  
  7.   
  8. void swap(int *a, int *b)  
  9. {  
  10.     int tmp = *a;  
  11.     *a = *b;  
  12.     *b = tmp;  
  13. }  
  14.   
  15. int partition(int a[], int low, int high)  
  16. {  
  17.     int privotKey = a[low];                 //基准元素  
  18.     while(low < high){                   //从表的两端交替地向中间扫描  
  19.         while(low < high  && a[high] >= privotKey) --high; //从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端  
  20.         swap(&a[low], &a[high]);  
  21.         while(low < high  && a[low] <= privotKey ) ++low;  
  22.         swap(&a[low], &a[high]);  
  23.     }  
  24.     print(a,10);  
  25.     return low;  
  26. }  
  27.   
  28.   
  29. void qsort_improve(int r[ ],int low,int high, int k){  
  30.     if( high -low > k ) { //长度大于k时递归, k为指定的数  
  31.         int pivot = partition(r, low, high); // 调用的Partition算法保持不变  
  32.         qsort_improve(r, low, pivot - 1,k);  
  33.         qsort_improve(r, pivot + 1, high,k);  
  34.     }   
  35. }   
  36. void quickSort(int r[], int n, int k){  
  37.     qsort_improve(r,0,n,k);//先调用改进算法Qsort使之基本有序  
  38.   
  39.     //再用插入排序对基本有序序列排序  
  40.     for(int i=1; i<=n;i ++){  
  41.         int tmp = r[i];   
  42.         int j=i-1;  
  43.         while(tmp < r[j]){  
  44.             r[j+1]=r[j]; j=j-1;   
  45.         }  
  46.         r[j+1] = tmp;  
  47.     }   
  48.   
  49. }   
  50.   
  51.   
  52.   
  53. int main(){  
  54.     int a[10] = {3,1,5,7,2,4,9,6,10,8};  
  55.     cout<<"初始值:";  
  56.     print(a,10);  
  57.     quickSort(a,9,4);  
  58.     cout<<"结果:";  
  59.     print(a,10);  
  60.   
  61. }  

 

7. 归并排序(Merge Sort)

 

基本思想:

归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

归并排序示例:

 技术分享

 

合并方法:

设r[i…n]由两个有序子表r[i…m]和r[m+1…n]组成,两个子表长度分别为n-i +1、n-m。

  1. j=m+1;k=i;i=i; //置两个子表的起始下标及辅助数组的起始下标
  2. 若i>m 或j>n,转⑷ //其中一个子表已合并完,比较选取结束
  3. //选取r[i]和r[j]较小的存入辅助数组rf
    如果r[i]<r[j],rf[k]=r[i]; i++; k++; 转⑵
    否则,rf[k]=r[j]; j++; k++; 转⑵
  4. //将尚未处理完的子表中元素存入rf
    如果i<=m,将r[i…m]存入rf[k…n] //前一子表非空
    如果j<=n ,  将r[j…n] 存入rf[k…n] //后一子表非空
  5. 合并结束。
[cpp] view plain copy
 
 print?
  1. //将r[i…m]和r[m +1 …n]归并到辅助数组rf[i…n]  
  2. void Merge(ElemType *r,ElemType *rf, int i, int m, int n)  
  3. {  
  4.     int j,k;  
  5.     for(j=m+1,k=i; i<=m && j <=n ; ++k){  
  6.         if(r[j] < r[i]) rf[k] = r[j++];  
  7.         else rf[k] = r[i++];  
  8.     }  
  9.     while(i <= m)  rf[k++] = r[i++];  
  10.     while(j <= n)  rf[k++] = r[j++];  
  11. }  
 

归并的迭代算法

 

1 个元素的表总是有序的。所以对n 个元素的待排序列,每个元素可看成1 个有序子表。对子表两两合并生成n/2个子表,所得子表除最后一个子表长度可能为1 外,其余子表长度均为2。再进行两两合并,直到生成n 个元素按关键码有序的表。

[cpp] view plain copy
 
 print?
  1. void print(int a[], int n){  
  2.     for(int j= 0; j<n; j++){  
  3.         cout<<a[j] <<"  ";  
  4.     }  
  5.     cout<<endl;  
  6. }  
  7.   
  8. //将r[i…m]和r[m +1 …n]归并到辅助数组rf[i…n]  
  9. void Merge(ElemType *r,ElemType *rf, int i, int m, int n)  
  10. {  
  11.     int j,k;  
  12.     for(j=m+1,k=i; i<=m && j <=n ; ++k){  
  13.         if(r[j] < r[i]) rf[k] = r[j++];  
  14.         else rf[k] = r[i++];  
  15.     }  
  16.     while(i <= m)  rf[k++] = r[i++];  
  17.     while(j <= n)  rf[k++] = r[j++];  
  18.     print(rf,n+1);  
  19. }  
  20.   
  21. void MergeSort(ElemType *r, ElemType *rf, int lenght)  
  22. {   
  23.     int len = 1;  
  24.     ElemType *q = r ;  
  25.     ElemType *tmp ;  
  26.     while(len < lenght) {  
  27.         int s = len;  
  28.         len = 2 * s ;  
  29.         int i = 0;  
  30.         while(i+ len <lenght){  
  31.             Merge(q, rf,  i, i+ s-1, i+ len-1 ); //对等长的两个子表合并  
  32.             i = i+ len;  
  33.         }  
  34.         if(i + s < lenght){  
  35.             Merge(q, rf,  i, i+ s -1, lenght -1); //对不等长的两个子表合并  
  36.         }  
  37.         tmp = q; q = rf; rf = tmp; //交换q,rf,以保证下一趟归并时,仍从q 归并到rf  
  38.     }  
  39. }  
  40.   
  41.   
  42. int main(){  
  43.     int a[10] = {3,1,5,7,2,4,9,6,10,8};  
  44.     int b[10];  
  45.     MergeSort(a, b, 10);  
  46.     print(b,10);  
  47.     cout<<"结果:";  
  48.     print(a,10);  
  49.   
  50. }  

两路归并的递归算法

[cpp] view plain copy
 
 print?
  1. void MSort(ElemType *r, ElemType *rf,int s, int t)  
  2. {   
  3.     ElemType *rf2;  
  4.     if(s==t) r[s] = rf[s];  
  5.     else  
  6.     {   
  7.         int m=(s+t)/2;          /*平分*p 表*/  
  8.         MSort(r, rf2, s, m);        /*递归地将p[s…m]归并为有序的p2[s…m]*/  
  9.         MSort(r, rf2, m+1, t);      /*递归地将p[m+1…t]归并为有序的p2[m+1…t]*/  
  10.         Merge(rf2, rf, s, m+1,t);   /*将p2[s…m]和p2[m+1…t]归并到p1[s…t]*/  
  11.     }  
  12. }  
  13. void MergeSort_recursive(ElemType *r, ElemType *rf, int n)  
  14. {   /*对顺序表*p 作归并排序*/  
  15.     MSort(r, rf,0, n-1);  
  16. }  

8. 桶排序/基数排序(Radix Sort)

说基数排序之前,我们先说桶排序:

基本思想:是将阵列分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递回方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的阵列内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。
         简单来说,就是把数据分组,放在一个个的桶中,然后对每个桶里面的在进行排序。  

 

 例如要对大小为[1..1000]范围内的n个整数A[1..n]排序  

 首先,可以把桶设为大小为10的范围,具体而言,设集合B[1]存储[1..10]的整数,集合B[2]存储   (10..20]的整数,……集合B[i]存储(   (i-1)*10,   i*10]的整数,i   =   1,2,..100。总共有  100个桶。  

  然后,对A[1..n]从头到尾扫描一遍,把每个A[i]放入对应的桶B[j]中。  再对这100个桶中每个桶里的数字排序,这时可用冒泡,选择,乃至快排,一般来说任  何排序法都可以。

  最后,依次输出每个桶里面的数字,且每个桶中的数字从小到大输出,这  样就得到所有数字排好序的一个序列了。  

  假设有n个数字,有m个桶,如果数字是平均分布的,则每个桶里面平均有n/m个数字。如果  

  对每个桶中的数字采用快速排序,那么整个算法的复杂度是  

  O(n   +   m   *   n/m*log(n/m))   =   O(n   +   nlogn   -   nlogm)  

  从上式看出,当m接近n的时候,桶排序复杂度接近O(n)  

  当然,以上复杂度的计算是基于输入的n个数字是平均分布这个假设的。这个假设是很强的  ,实际应用中效果并没有这么好。如果所有的数字都落在同一个桶中,那就退化成一般的排序了。  

        前面说的几大排序算法 ,大部分时间复杂度都是O(n2),也有部分排序算法时间复杂度是O(nlogn)。而桶式排序却能实现O(n)的时间复杂度。但桶排序的缺点是:

        1)首先是空间复杂度比较高,需要的额外开销大。排序有两个数组的空间开销,一个存放待排序数组,一个就是所谓的桶,比如待排序值是从0到m-1,那就需要m个桶,这个桶数组就要至少m个空间。

        2)其次待排序的元素都要在一定的范围内等等。

       桶式排序是一种分配排序。分配排序的特定是不需要进行关键码的比较,但前提是要知道待排序列的一些具体情况。

 

分配排序的基本思想:说白了就是进行多次的桶式排序。

基数排序过程无须比较关键字,而是通过“分配”和“收集”过程来实现排序。它们的时间复杂度可达到线性阶:O(n)。

实例:

扑克牌中52 张牌,可按花色和面值分成两个字段,其大小关系为:
花色: 梅花< 方块< 红心< 黑心  技术分享
面值: 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < J < Q < K < A

若对扑克牌按花色、面值进行升序排序,得到如下序列:
技术分享

技术分享


即两张牌,若花色不同,不论面值怎样,花色低的那张牌小于花色高的,只有在同花色情况下,大小关系才由面值的大小确定。这就是多关键码排序。

为得到排序结果,我们讨论两种排序方法。
方法1:先对花色排序,将其分为4 个组,即梅花组、方块组、红心组、黑心组。再对每个组分别按面值进行排序,最后,将4 个组连接起来即可。
方法2:先按13 个面值给出13 个编号组(2 号,3 号,...,A 号),将牌按面值依次放入对应的编号组,分成13 堆。再按花色给出4 个编号组(梅花、方块、红心、黑心),将2号组中牌取出分别放入对应花色组,再将3 号组中牌取出分别放入对应花色组,……,这样,4 个花色组中均按面值有序,然后,将4 个花色组依次连接起来即可。

设n 个元素的待排序列包含d 个关键码{k1,k2,…,kd},则称序列对关键码{k1,k2,…,kd}有序是指:对于序列中任两个记录r[i]和r[j](1≤i≤j≤n)都满足下列有序关系:

                                                               技术分享

其中k1 称为最主位关键码,kd 称为最次位关键码     。

 

两种多关键码排序方法:

多关键码排序按照从最主位关键码到最次位关键码或从最次位到最主位关键码的顺序逐次排序,分两种方法:

最高位优先(Most Significant Digit first)法,简称MSD 法:

1)先按k1 排序分组,将序列分成若干子序列,同一组序列的记录中,关键码k1 相等。

2)再对各组按k2 排序分成子组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd 对各子组排序后。

3)再将各组连接起来,便得到一个有序序列。扑克牌按花色、面值排序中介绍的方法一即是MSD 法。

最低位优先(Least Significant Digit first)法,简称LSD 法:

1) 先从kd 开始排序,再对kd-1进行排序,依次重复,直到按k1排序分组分成最小的子序列后。

2) 最后将各个子序列连接起来,便可得到一个有序的序列, 扑克牌按花色、面值排序中介绍的方法二即是LSD 法。

 

基于LSD方法的链式基数排序的基本思想

  “多关键字排序”的思想实现“单关键字排序”。对数字型或字符型的单关键字,可以看作由多个数位或多个字符构成的多关键字,此时可以采用“分配-收集”的方法进行排序,这一过程称作基数排序法,其中每个数字或字符可能的取值个数称为基数。比如,扑克牌的花色基数为4,面值基数为13。在整理扑克牌时,既可以先按花色整理,也可以先按面值整理。按花色整理时,先按红、黑、方、花的顺序分成4摞(分配),再按此顺序再叠放在一起(收集),然后按面值的顺序分成13摞(分配),再按此顺序叠放在一起(收集),如此进行二次分配和收集即可将扑克牌排列有序。   

基数排序:

是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。

算法实现:

[cpp] view plain copy
 
 print?
  1. Void RadixSort(Node L[],length,maxradix)  
  2. {  
  3.    int m,n,k,lsp;  
  4.    k=1;m=1;  
  5.    int temp[10][length-1];  
  6.    Empty(temp); //清空临时空间  
  7.    while(k<maxradix) //遍历所有关键字  
  8.    {  
  9.      for(int i=0;i<length;i++) //分配过程  
  10.     {  
  11.        if(L[i]<m)  
  12.           Temp[0][n]=L[i];  
  13.        else  
  14.           Lsp=(L[i]/m)%10; //确定关键字  
  15.        Temp[lsp][n]=L[i];  
  16.        n++;  
  17.    }  
  18.    CollectElement(L,Temp); //收集  
  19.    n=0;  
  20.    m=m*10;  
  21.   k++;  
  22.  }  
  23. }  


 

 

 

 

总结

各种排序的稳定性,时间复杂度和空间复杂度总结:

技术分享

 我们比较时间复杂度函数的情况:

技术分享

 

                             时间复杂度函数O(n)的增长情况

技术分享

所以对n较大的排序记录。一般的选择都是时间复杂度为O(nlog2n)的排序方法。

 

时间复杂度来说:

(1)平方阶(O(n2))排序
  各类简单排序:直接插入、直接选择和冒泡排序;
 (2)线性对数阶(O(nlog2n))排序
  快速排序、堆排序和归并排序;
 (3)O(n1+§))排序,§是介于0和1之间的常数。

       希尔排序
(4)线性阶(O(n))排序
  基数排序,此外还有桶、箱排序。

说明:

当原表有序或基本有序时,直接插入排序和冒泡排序将大大减少比较次数和移动记录的次数,时间复杂度可降至O(n);

而快速排序则相反,当原表基本有序时,将蜕化为冒泡排序,时间复杂度提高为O(n2);

原表是否有序,对简单选择排序、堆排序、归并排序和基数排序的时间复杂度影响不大。

 

稳定性:

排序算法的稳定性:若待排序的序列中,存在多个具有相同关键字的记录,经过排序, 这些记录的相对次序保持不变,则称该算法是稳定的;若经排序后,记录的相对 次序发生了改变,则称该算法是不稳定的。 
     稳定性的好处:排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。基数排序就是这样,先按低位排序,逐次按高位排序,低位相同的元素其顺序再高位也相同时是不会改变的。另外,如果排序算法稳定,可以避免多余的比较;

稳定的排序算法:冒泡排序、插入排序、归并排序和基数排序

不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序

 

选择排序算法准则:

每种排序算法都各有优缺点。因此,在实用时需根据不同情况适当选用,甚至可以将多种方法结合起来使用。

选择排序算法的依据

影响排序的因素有很多,平均时间复杂度低的算法并不一定就是最优的。相反,有时平均时间复杂度高的算法可能更适合某些特殊情况。同时,选择算法时还得考虑它的可读性,以利于软件的维护。一般而言,需要考虑的因素有以下四点:

1.待排序的记录数目n的大小;

2.记录本身数据量的大小,也就是记录中除关键字外的其他信息量的大小;

3.关键字的结构及其分布情况;

4.对排序稳定性的要求。

设待排序元素的个数为n.

1)当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。

   快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
       堆排序 :  如果内存空间允许且要求稳定性的,

       归并排序:它有一定数量的数据移动,所以我们可能过与插入排序组合,先获得一定长度的序列,然后再合并,在效率上将有所提高。

2)  当n较大,内存空间允许,且要求稳定性 =》归并排序

3)当n较小,可采用直接插入或直接选择排序。

    直接插入排序:当元素分布有序,直接插入排序将大大减少比较次数和移动记录的次数。

    直接选择排序 :元素分布有序,如果不要求稳定性,选择直接选择排序

5)一般不使用或不直接使用传统的冒泡排序。

6)基数排序
它是一种稳定的排序算法,但有一定的局限性:
  1、关键字可分解。
  2、记录的关键字位数较少,如果密集更好
  3、如果是数字时,最好是无符号的,否则将增加相应的映射复杂度,可先将其正负分开排序。

 

注明:转载请提示出处:http://blog.csdn.net/hguisu/article/details/7776068
 
194
1
 

我的同类文章

 
更多文章
 
猜你在找
查看评论
84楼 谁当是玩游戏 4天前 21:56发表 [回复]
技术分享
谢谢楼主的分享,但是有些地方还是有很致命的错误的。直接插入排序那里应该为
if( a[i+1] < a[i] )
{
int j = i;
int x = a[i+1];
while( x < a[j] )
{
a[j+1] = a[j];
j--;
}
a[j+1] = x;
}
否则i-1有可能为负值。
83楼 查洛巴 2016-07-23 23:15发表 [回复]
技术分享
楼主最后的总结:
堆排序 : 如果内存空间允许且要求稳定性的,
这不正确。
82楼 qq_24321917 2016-07-18 14:50发表 [回复]
技术分享
关于算法的实质我会呢,就是在写代码的是怎么不会,没有头绪,让我排序,我能用算法的实质做出来,但是用代码就是不会啊,怎么办?求解
81楼 FlappyXiang 2016-07-14 14:50发表 [回复]
技术分享
"设r[i…n]由两个有序子表r[i…m]和r[m+1…n]组成,两个子表长度分别为n-i +1、n-m" 中两个子表长度写错了,应该是“两个子表长度分别为m-i +1、n-m”
80楼 VASNOW 2016-06-14 15:14发表 [回复]
技术分享
二原选择排序出错 当例子为{9,8,7,6,5,4,3,2,1,0}时
79楼 VASNOW 2016-06-14 13:47发表 [回复]
技术分享
希尔排序那里a[j+dk] = a[j];应改为a[j] = a[j-dk];
虽然对结果无影响,但每次都多了一次无意义的移位
78楼 xianyuxiangkou 2016-06-12 10:54发表 [回复]
技术分享
对于桶排序我也是没看懂,你这是什么桶排序,我怎么没见过?桶排序内部可用冒泡,选择,乃至快排?你又何必用桶呢?直接用这些排序就是了。
77楼 xianyuxiangkou 2016-06-12 10:28发表 [回复]
技术分享
不是很理解楼主写得那个正向和反向同时进行的冒泡排序。和传统的比,比较的次数一次没少,还判断不了最好情况(有序数列O(n)的情况)
Re: 晓江南 2016-08-06 16:59发表 [回复]
技术分享
回复xianyuxiangkou:冒泡排序的改进二,同感觉循环次数并没有改变啊
76楼 12期一刘超 2016-06-11 10:06发表 [回复]
技术分享
楼主辛苦
75楼 十二-李晓洁 2016-06-05 16:11发表 [回复]
技术分享
博主写了这么多不容易
74楼 roamer_nuptgczx 2016-05-28 16:50发表 [回复]
技术分享
首先,楼主写了这么多辛苦了!有些总结得不错,但是提供的代码最好自己先测试下,有些写的比较繁琐,比如直接插入排序,有些还有错误,比如迭代的归并排序。我总结了一下常见的排序算法的源码并写了详细的注释,希望能和楼主交流下
http://blog.csdn.net/roamer_nuptgczx/article/details/51524466
73楼 miangmiang咩 2016-05-09 10:51发表 [回复]
技术分享
归并排序,数组长度为奇数时候有问题呀
Re: 梁同桌 2016-07-22 00:12发表 [回复]
技术分享
回复miangmiang咩:void MergeSort(int *r, int *rf, int lenght){ int len = 1; int *q = r ; int *tmp ; while(len < lenght) { int s = len; len = 2 * s ; int i = 0; while(i+ len <lenght){ Merge(q, rf, i, i+ s-1, i+ len-1 ); //对等长的两个子表合并 i = i+ len; } if(i + s < lenght+1){ // 这里要+1 Merge(q, rf, i, i+ s-1, lenght -1); //对不等长的两个子表合并 } tmp = q; q = rf; rf = tmp; //交换q,rf,以保证下一趟归并时,仍从q 归并到rf }}int main(){ int a[19] = {3,1,5,7,2,4,9,6,8,0,20,3,5,6,7,-1,-1,-2,0}; int b[19]; MergeSort(a, b,19); cout<<"结果:"; print(b,19); }
72楼 miangmiang咩 2016-05-09 09:56发表 [回复]
技术分享
tmp = q; q = rf; rf = tmp; //交换q,rf,以保证下一趟归并时,仍从q 归并到rf 
我对于这句话有个疑问:
MergeSort(a, b, 10); 
其中b应该是辅助数组,但是我添加出来的那句话里,不会导致,最后自己都不清楚排序好的是数组a还是数组b?
71楼 chs0113 2016-05-05 17:14发表 [回复]
技术分享
给力 get了
70楼 程续缘 2016-04-21 19:20发表 [回复]
技术分享
文中冒泡算法改进1似乎是没有效率提升的,我在元素比对时进行了count++的操作,最终的count数与原始版本的一致。
我的测试数组:{ 4, 7, 8, 9, 3, 2 }
这是原始版本的冒泡:最终count为15
  1. public static void bubbleSort(int[] arg) {  
  2.     int count = 0;  
  3.     for (int i = 0; i < arg.length - 1; i++) {  
  4.         for (int j = 0; j < arg.length - i - 1; j++) {  
  5.         count++;  
  6.         if (arg[j] > arg[j + 1]) {  
  7.             int temp = arg[j];  
  8.             arg[j] = arg[j + 1];  
  9.             arg[j + 1] = temp;  
  10.         }  
  11.         }  
  12.     }  
  13.     System.out.println(count);  
  14.     }  

这是冒泡改进1,最终count也是15
  1. <br> public static void bubbleSort1(int[] array) {<br>  // 初始时,最后位置保持不变<br> int i = array.length - 1;<br>   int count = 0;<br>  while (i > 0) {<br>      // 每趟开始时,无记录交换<br>      int pos = 0;<br>        for (int j = 0; j < i; j++) {<br>        if (array[j] > array[j + 1]) {<br>           // 记录交换的位置<br>          pos = j;<br>            int temp = array[j];<br>            array[j] = array[j + 1];<br>            array[j + 1] = temp;<br>            <br>        }<br>       count++;<br>        }<br>       // 为下一趟排序作准备<br>        i = pos;<br>    }<br>   System.out.println(count);<br>    }<br>  

请指教。
Re: 笨笨的博客 2016-07-23 21:38发表 [回复]
技术分享
回复程续缘: 这个改进只对后面部分基本有序有效果,你改成4,7,8,9,9,9就可以看出效果了
69楼 lt_迹忆 2016-04-19 13:19发表 [回复]
技术分享
感谢博主的分享。这里我也总结了几种常用排序的算法,还请博主多多指教 http://www.onmpw.com/tm/xwzj/algorithm_112.html
68楼 CUG_Bear 2016-04-18 21:29发表 [回复]
技术分享
  1. void insertSort(int a[], int n) {  
  2.     for (int i = 1; i < n; i++) {  
  3.         if (a[i] < a[i - 1]) {  
  4.             int j = i - 1;  
  5.             int x = a[i];  
  6.                     //这里这行代码没有用  
  7.             while (x < a[j]) {  
  8.                 a[j + 1] = a[j];  
  9.                 j--;  
  10.             }  
  11.             a[j + 1] = x;  
  12.         }  
  13.     }  
  14. }  
67楼 qq_18115123 2016-04-18 10:44发表 [回复]
技术分享
直接插入排序中, while(x < a[j]) //查找在有序表的插入位置 应该改为 while(x<a[j] && j>=0)
Re: shuai1210 2016-04-28 16:59发表 [回复]
技术分享
回复qq_18115123:应该先判断索引 j>=0
66楼 fw1993 2016-04-08 00:21发表 [回复]
技术分享
楼主 归并排序 的递归实现里 
ElemType *rf2;
rf2 是不是存的临时结果啊,这样不申请空间不会有内存问题吗?

是不是要写成 rf2 = new ElemType....?
65楼 fw1993 2016-04-08 00:20发表 [回复]
技术分享
楼主 归并排序 的递归实现里 
ElemType *rf2;
rf2 是不是存的临时结果啊,这样不申请空间不会有内存问题吗?

是不是要写成 rf2 = new ElemType....?
64楼 qq_25198573 2016-04-07 21:10发表 [回复]
技术分享
楼主,貌似堆排序中的
int child = 2*s+1;错了吧,该语句已经将child指向右节点了。
我认为应该把所有child下滤的语句改成child=child*2;同时初始化中将child=2*s,确保child指向的是左节点。

int temp=arr[i];
int child=i*2;
while(child<len)
{
if(child+1<len&&arr[child]>arr[child+1])
child++;
if(arr[i]>arr[child])
{
arr[i]=arr[child];
i=child;
child=child*2;
}
else break;
arr[i]=temp;
}
代码如上;
Re: gentle_king 2016-04-25 11:19发表 [回复]
技术分享
回复qq_25198573:没错的,因为第一个元素下标是0,所以,第i个根节点的左子节点下标就是2*i+1;如果第一个元素下标是1,那么第i个根节点的左子节点下标才是2*i。
63楼 huangyue2014 2016-04-06 14:49发表 [回复]
技术分享
插入排序:
/**
* 插入排序
*/
public static void insertSort()
{
int[] arrs = {49,38,65,97,76,13,27};
for(int i=1;i<arrs.length;i++)
{
if(arrs[i] >= arrs[i-1])
continue;
else
{
int k = 0;
while(i-1>=0 && arrs[i] < arrs[i-1])
{
int temp = arrs[i];
arrs[i] = arrs[i-1];
arrs[i-1] = temp;
i--;
k++;
}
i=i+k;
}
}
}
62楼 maowei_123 2016-03-19 11:17发表 [回复]
技术分享
选择排序的改进-二元选择排序写错了,知识因为博客的给的数组恰好掩盖了错误,这是我正确的代码,用java实现:
public class Main 
public static void main(String[] args){
int[] a = {8,2,5,4,1,9,10,3,7,12,6,4,123,5,32,12234};
SelectSort(a, a.length);
}
public static void SelectSort(int r[], int n){ 
int i ,j , min ,max, tmp; 
for (i=0 ;i <= n/2;i++) { 
// 做不超过n/2趟选择排序 
min = i; max = n-i-1; //分别记录最大和最小关键字记录位置 
for (j= i+1; j< n-i; j++) { 
if (r[j] > r[max]) { 
max = j ;
continue ; 

if (r[j]< r[min]) { 
min = j ; 


//该交换操作还可分情况讨论以提高效率 
tmp = r[i]; r[i] = r[min]; r[min] = tmp; 
tmp = r[n - i -1]; r[n - i -1] = r[max]; r[max] = tmp;
print(r); //打印每趟排序的结果 

}
public static void print(int[] a){
for (int i : a) {
System.out.print(i + " ");
}
System.out.println("");
}
}
Re: qq_26014757 2016-06-19 17:23发表 [回复]
技术分享
回复maowei_123:不对,第一个元素不是最大,也还可能出错
Re: qq_26014757 2016-06-19 17:16发表 [回复]
技术分享
回复maowei_123:我发现一个神奇的现象,只要当数组第一个元素为组中最大时,二元插入算法会出错,其他情况就没事,你们说为什么
61楼 饕餮的阿周 2016-03-15 17:47发表 [回复]
技术分享
太给力了,谢谢楼主
60楼 zcxyl 2016-03-15 11:33发表 [回复]
技术分享
楼主总结的很棒。之前没仔细研究过堆排序,看了以后发现堆排序性能很高。
堆排序那节的堆调整函数,我感觉这样写会更简洁易懂
  1. #define SWAP(x,y) do \  
  2.                   { \  
  3.                     int tmp = x; \  
  4.                     x = y; \  
  5.                     y = tmp; \  
  6.                   }while(0)  
  7.   
  8. #define L_CHILD (2 * (root) + 1)  
  9. #define R_CHILD (2 * (root) + 2)  
  10.   
  11. static void heap_adjust(int *heap, int root, int len)  
  12. {  
  13.     if (L_CHILD >= len)  
  14.         return;  
  15.       
  16.     int swap = L_CHILD;  
  17.     if (R_CHILD < len) {  
  18.         swap = (heap[L_CHILD] < heap[R_CHILD])?L_CHILD:R_CHILD;  
  19.     }  
  20.       
  21.     if (heap[root] > heap[swap]) {  
  22.         SWAP(heap[root], heap[swap]);  
  23.         heap_adjust(heap, swap, len);  
  24.     }  
  25. }  
59楼 行者小朱 2016-03-07 16:26发表 [回复]
技术分享
楼主总结的不错
58楼 lst666 2016-02-25 12:15发表 [回复]
技术分享
+1
57楼 KingXLee 2016-02-24 17:53发表 [回复]
技术分享
很好~~
56楼 fangziqh 2016-02-15 12:02发表 [回复]
技术分享
你好,我今天试了一下你的快速排序改进算法,其中j的范围没有保证,应该有一个判断条件,j>=0
55楼 麦兜NC粉 2016-01-09 11:52发表 [回复]
技术分享
都没人说说堆排序吗 ?
HeapAdjust(int H[],int s, int length) 中 ,H[s] = tmp;应该放在while循环的外面吧
Re: zcxyl 2016-03-15 11:40发表 [回复]
技术分享
回复麦兜NC粉:楼主没写错
54楼 lushangqiushui 2015-12-16 15:23发表 [回复]
技术分享
5.2改进冒泡好像会越界,具体没验证,我自己是这样写的:
void BubbleSort2(int aBuf[], int nLen)
{
bool bEnd=true;

int nLow=0;
int nHigh=nLen-1;
int nTmp=0;

while(nLow < nHigh)
{
bEnd=false;

for(int nIdx=nLow; nIdx<nHigh-1; ++nIdx)
{
if(aBuf[nIdx]>aBuf[nIdx+1])
{
nTmp=aBuf[nIdx];
aBuf[nIdx]=aBuf[nIdx+1];
aBuf[nIdx+1]=nTmp;

bEnd=false;
}
}

for(int nIdx=nHigh; nIdx>nLow+1; --nIdx)
{
if(aBuf[nIdx]<aBuf[nIdx-1])
{
nTmp=aBuf[nIdx];
aBuf[nIdx]=aBuf[nIdx-1];
aBuf[nIdx-1]=nTmp;

bEnd=false;
}
}

--nHigh;
++nLow;

if(bEnd) break;
}
}

自己验证没问题,旁观者清,我可能会当局者迷,不知楼主有没有其他见解或指教?
53楼 baidu_29162549 2015-12-13 14:17发表 [回复]
技术分享
排序中一和二是否有点出入,while循环内是否应该加一个if判断语句
52楼 三目君 2015-11-11 15:37发表 [回复]
技术分享
快速排序是原址排序,它的空间复杂度应当是O(1)。
1. 你的快速排序源码中,辅助空间仅有一个int privotKey = a[low]; 是常数个数。
2. 参考《算法导论(第3版)》82页中文版原文:“第7章介绍快速排序,它也是一种原址排序算法”
Re: xianyuxiangkou 2016-06-12 11:05发表 [回复]
技术分享
回复三目君:快速排序算法是通过递归实现的,实现递归一定是有栈的。栈的高度是log2n。需要O(1)空间开销的排序叫原地排序,不叫原址排序,这是两个不同的概念。
51楼 mayfla 2015-10-26 14:24发表 [回复]
技术分享
总结好用心啊,一直有一个疑问,排序在计算机中占了什么样的位置,为什么要有排序呢?
Re: 吃不够的圆儿宝 2015-10-30 15:11发表 [回复]
技术分享
回复mayfla:排序是一切算法的基础,比如你要二分查找,那么就必须先排序,然后对排序数组进行二分查找
50楼 丁国华 2015-10-22 16:57发表 [回复]
技术分享
感谢楼主的分享,学习了`(*∩_∩*)′
49楼 杨晨光 2015-10-11 11:39发表 [回复]
技术分享
总结的相当NB啊,楼主
48楼 Honei_X 2015-10-10 00:32发表 [回复]
技术分享
写的挺好的,难得把这么多的排序算法都写了一下。挺辛苦的。
47楼 Honei_X 2015-10-10 00:32发表 [回复]
技术分享
写的挺好的,难得把这么多的排序算法都写了一下。挺辛苦的。
46楼 tiger3c 2015-10-09 14:27发表 [回复]
技术分享
插入排序中j--会导致数组下标出现-1,如果数组中最后几位有负数则无法排序。改正后的代码如下:
void InsertSort(int a[], int n) 

for(int i= 1; i<n; i++){ 
if(a[i] < a[i-1]){ //若第i个元素大于i-1元素,直接插入。小于的话,移动有序表后插入 
int j= i-1; 
int x = a[i]; //复制为哨兵,即存储待排序元素 
a[i] = a[i-1]; //先后移一个元素 
cout<<"a[i-1]:"<<a[i-1]<<endl;
while(x < a[j] ){ //查找在有序表的插入位置 
a[j+1] = a[j]; 
j--; //元素后移 
if(j < 0) break; //防止下标出现-1仍然继续while循环会导致程序崩溃当数组内容类似为:{3,2,5,7,1,-4,8,6}时

a[j+1] = x; //插入到正确位置 

print(a,n,i); //打印每趟排序的结果 


}
Re: lst666 2016-02-25 12:14发表 [回复]
技术分享
回复tiger3c:a[-1]也不是不行,但是最好不这样,就是内存中一般这个位置一般是垃圾,读出来是个负数。所以,这段实现勉强能运行,如果先a[-1]=100,那这个程序结果就是错的。
while( j>=0 && x < a[j]) 可以这样改下,测试可用。
Re: 书生起义 2015-12-04 09:33发表 [回复]
技术分享
回复tiger3c:你的不对吧,
45楼 唐凌峰 2015-10-05 11:13发表 [回复]
技术分享
在学习中,感谢你的分享
44楼 箫沐予 2015-09-17 12:05发表 [回复]
技术分享
蛮好的,有参照。对于插入排序,因为我是用JS来仿写各种算法,所以while(x<a[j]) 这点上,反而很方便正确。因为当j减到-1后,a[-1]等于undefinded, x<a[j]判断的结果为false,不会再进入循环。
43楼 jingzhemodao 2015-09-11 10:24发表 [回复]
技术分享
你的插入排序写错了,知道不。
42楼 gs0038 2015-09-10 17:53发表 [回复]
技术分享
冒泡写错了
for(int i =0 ; i< n-1; ++i) { 
for(int j = i; j < n-i-1; ++j) { 
if(a[i] > a[j+1]) 

int tmp = a[i] ; a[i] = a[j+1] ; a[j+1] = tmp; 



这样子才对~
Re: tiger3c 2015-10-09 15:19发表 [回复]
技术分享
回复gs0038:按照你的代码请试着排一下如下数组:
int a[8] = {3,2,5,7,1,4,8,6}; 

运行结果是:1 2 3 5 7 4 8 6显然是错误的
Re: tiger3c 2015-10-09 15:14发表 [回复]
技术分享
回复gs0038:楼主的程序没错,你提供的修改中代码中既有外循环的下标i又有内循环的下标j好乱,是否有验证过再发表意见?
正确的代码就是:
void bubbleSort(int a[], int n){ 
for(int i =0 ; i< n-1; ++i) { 
for(int j = 0; j < n-i-1; ++j) { 
if(a[j] > a[j+1]) 

int tmp = a[j] ; a[j] = a[j+1] ; a[j+1] = tmp; 



}
41楼 zfxrj 2015-08-30 16:36发表 [回复]
技术分享
看了第一个就不想看了!
40楼 Prepare_y 2015-08-05 16:02发表 [回复]
技术分享
选择排序while()里应该加一个条件,j>-1,不然程序不对
39楼 欢乐的工科小硕 2015-07-27 19:49发表 [回复]
技术分享
mark 来 看一下
38楼 nihaoljq2010 2015-07-12 17:39发表 [回复]
技术分享
感谢楼主分享!
我将每个算法运行了一遍,其中nlogn算法用亿级数量级;
已全部实现。
中间一些算法有误,提示一下楼主:
如归并算法,在待排序数量大于1万时候排序结果会有问题。

修改前: tmp = q; q = rf; rf = tmp; 
修改后:
1、MergeSort中,先将rf初始化
for(int k=0;k<lenArr;k++)
rf[k]=r[k];
2、MergeSort中,将tmp = q; q = rf; rf = tmp; 语句修改为
for(int k=0;k<lenArr;k++)
q[k]=rf[k];
Re: wzysws 2015-09-06 17:09发表 [回复]
技术分享
回复nihaoljq2010:楼主改得好,赞一个。
Re: cpu_12593 2015-08-30 11:15发表 [回复]
技术分享
回复nihaoljq2010:博主的归并迭代排序是有问题,当归并的次数是奇数是排序的结果保存在rf中,当为偶数时结果才在r中。因此在迭代的最后不应该交换指针,而是交换元素的值。
Re: wzysws 2015-09-06 17:08发表 [回复]
技术分享
回复cpu_12593:跟归并的次数无关,而是跟指针交换的次数。交换偶数次存rf中,奇数次存r中。
37楼 刘小壮 2015-06-23 12:00发表 [回复]
技术分享
  1. void sort1()  
  2. {  
  3.     int a[] = {3,5,67,14,4,6,1,7};  
  4.     for (int i = 1; i < 8; i++) {  
  5.           
  6.         if (a[i] < a[i-1]) {  
  7.             int j = i-1;  
  8.             int x = a[i];  
  9.             a[i] = a[i-1];  
  10.             while(x < a[j-1]){  
  11.                 a[j] = a[j-1];  
  12.                 j--;  
  13.             }  
  14.             a[j] = x;  
  15.         }  
  16.     }  
  17.     print(a, 8);  
  18. }  

楼主,我上面这段代码的代码逻辑没问题,但是结果却不对,请教一下你。
36楼 自犬邦 2015-05-23 20:58发表 [回复]
技术分享
博主 
tmp = q; q = rf; rf = tmp; 

rf是首地址,不能直接赋值的啊
35楼 ZYQblog1989 2015-05-10 18:21发表 [回复]
技术分享
您实现的插入算法稍微有点问题,其实只要从第2个开始逐个与前面的比对,到头后再逐渐向后移动光标即可,如下(OC实现的,阅读起来应该无障碍):
- (void)InsertSortWithNumbers:(NSMutableArray *)nums {

NSLog(@"before the sort %@", nums);

for (int i = 1; i < nums.count; i++) {

for (int j = i; j >= 1; j --) {

NSNumber *numj = nums[j];
NSNumber *numjex = nums[j - 1];

if ([numj intValue] < [numjex integerValue]) {
NSNumber *numjexc = [numjex copy];

numjex = [NSNumber numberWithInteger:numj.integerValue];
numj = [NSNumber numberWithInteger:numjexc.integerValue];
nums[j] = numj;
nums[j - 1] = numjex;

} else {

break;
}
}

NSLog(@"after once sort %@", nums);
}
}
34楼 ZYQblog1989 2015-05-10 18:06发表 [回复]
技术分享
引用“oAriesn”的评论:第一个插入排序中有个错误:while 循环的地方应该改为while(x &lt; a[j]&amp;...

确实,我跑了一遍发现这里缺少判断条件
33楼 oAriesn 2015-04-23 19:04发表 [回复]
技术分享
第一个插入排序中有个错误:while 循环的地方应该改为while(x < a[j]&&j>=0)
32楼 拾毅者 2015-04-21 19:32发表 [回复]
技术分享
楼主希尔排序有问题
正确如下
/*
* 5.希尔排序 实现代码方法一
*/
public void shellSort(Integer []array){
//{1,33,3,14,5,71,6,88}
int d=array.length/2;
while(d >= 1){
for (int i = d; i < array.length; ++i) {
if(array[i]<array[i-d]){
int b=i-d;
int temp = array[i];
while(b>=0 && temp<array[b]) {
array[b+d] = array[b];
b-=d;
}
array[b+d] = temp;
}
}
d=d/2;
//=========输出排序详情
System.out.print("shell" + d+":");
for (int j = 0; j < array.length; j++) {
System.out.print(array[j] + " ");
}
System.out.println();
}
}
31楼 Finlay 2015-04-05 12:49发表 [回复]
技术分享
你的冒泡改进版在数据少情况下好一点,在数据大的情况下性能非常差,比一般冒泡都差。

我分析应该是数据局部性原理所致,数据量小,cache直接命中;数据量太大,你的冒泡改进算法cache丢失太严重,性能差。
30楼 l_xm 2015-04-02 21:58发表 [回复]
技术分享
有点小错误,但还是很有帮组。特别是时间复杂度的分析。
29楼 SpringWar 2015-04-01 21:40发表 [回复]
技术分享
很有用,先mark下
28楼 ABC19901215 2015-03-25 09:43发表 [回复]
技术分享
我发现快排是错的,如果输入是54321的话,按照目前的代码无法搞定
27楼 foverpulove 2015-03-11 16:13发表 [回复]
技术分享
mark
26楼 天荒地老 2015-03-10 00:42发表 [回复]
技术分享
mark
25楼 ls2071xiaohua 2015-02-17 21:29发表 [回复]
技术分享
选择排序的4。5一样的?还是写错了呢?
24楼 ls2071xiaohua 2015-02-17 21:24发表 [回复]
技术分享
我希尔排序的第三遍没看懂,如果我没理解错,第一遍是16比,第二遍14比,第三遍13比,那么,13和04 是不是反了?还是我理解错了
23楼 Lucien_Leng 2015-01-17 22:21发表 [回复]
技术分享
楼主代码有错,而且不止一处啊!!!
22楼 浊公子 2014-12-07 20:29发表 [回复]
技术分享
楼主,直接插入排序好像错了
21楼 gaojiecaiandroid 2014-11-05 20:13发表 [回复]
技术分享
博主的直接插入有问题,正确的如下:
public class InsertSort3 {
static int[] a = { 100, 12, 97, 76, 13, 27, 24, 645, 23, 656, 121, 656, 121 };

public static void main(String[] args) {
sort(a, a.length);
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}

}

public static void sort(int[] arr, int n) {
int x;int j;
for (int i = 1; i < n; i++) {
j = i - 1; // 要插入元素的下标
x = arr[i]; // 哨兵

while(j>=0 && x<a[j]){
a[j+1] = a[j];
j--;
}
a[j + 1] = x; // 插入到正确的位置
}
}
}
20楼 gaojiecaiandroid 2014-11-05 20:11发表 [回复]
技术分享
public class InsertSort3 {
static int[] a = { 100, 12, 97, 76, 13, 27, 24, 645, 23, 656, 121, 656, 121 };

public static void main(String[] args) {
sort(a, a.length);
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}

}

public static void sort(int[] arr, int n) {
int x;int j;
for (int i = 1; i < n; i++) {
j = i - 1; // 要插入元素的下标
x = arr[i]; // 哨兵

for( ; j>=0 && x<a[j]; j--){
a[j+1] = a[j];
}
a[j + 1] = x; // 插入到正确的位置
}
}
}
19楼 gaojiecaiandroid 2014-11-05 20:10发表 [回复]
技术分享
楼主的直接插入有问题,会数组溢出的...下面是正确的方式,加一个j>=0的条件。
public static void sort(int[] arr, int n) {
int x;int j;
for (int i = 1; i < n; i++) {
j = i - 1; // 要插入元素的下标
x = arr[i]; // 哨兵

for( ; j>=0 && a[j+1]<a[j]; j--){
a[j+1] = a[j];
}
a[j + 1] = x; // 插入到正确的位置
}
}
18楼 gaojiecaiandroid 2014-11-01 14:18发表 [回复]
技术分享
"合并方法:

设r[i…n]由两个有序子表r[i…m]和r[m+1…n]组成,两个子表长度分别为n-i +1、n-m。"

这里写错了吧,博主,俩个子表长度分别为m、n-m吧?
17楼 平姐 2014-10-27 19:44发表 [回复]
技术分享
堆排序中,最后一个有孩子的节点位置应该写成length/2 - 1更好吧
Re: 雅阁驸马 2015-07-14 22:06发表 [回复]
技术分享
回复平姐:对,应该这样写,楼主那样写奇数个数的时候找到的不是最后一个非叶子节点
Re: gaojiecaiandroid 2014-11-07 14:21发表 [回复]
技术分享
回复平姐:我也这样认为啊,博主写得那样有问题根本就是
16楼 cqu_software 2014-09-28 14:52发表 [回复]
技术分享
标记,有用
15楼 herojia_1 2014-09-27 16:29发表 [回复]
技术分享
直接插入排序:
int j = i-2;
while( j>=0 && x <a[ j ])
{}
14楼 wooder_mu 2014-09-25 18:03发表 [回复]
技术分享
好老的帖子。。。
但是!
快排要重新将基准值插入中间位置

int partition(int a[], int low, int high) 

int privotKey = a[low]; //基准元素 
while(low < high){ //从表的两端交替地向中间扫描 
while(low < high && a[high] >= privotKey) --high; //从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端 
swap(&a[low], &a[high]); 
while(low < high && a[low] <= privotKey ) ++low; 
swap(&a[low], &a[high]); 

print(a,10); 
a[low] = privotKey;//快排要重新将基准值插入中间位置
return low; 
}
Re: liuyongli1992 2016-01-04 22:47发表 [回复]
技术分享
回复wooder_mu:你插入这个是多此一举吧,上面swap已经将基准值放到了中间位置
13楼 风刮过冷 2014-09-24 13:39发表 [回复]
技术分享
很感谢博主,但不知道博主都一一试过这些算法没有,错误有点多啊,
Re: 真实的归宿 2014-09-24 14:41发表 [回复]
技术分享
回复风刮过冷:该博文总结《数据结构》提到的算法。但有的例子代码没有运行。
12楼 QueJay 2014-09-16 14:44发表 [回复]
技术分享
很棒,收藏了
11楼 jasonyuan18 2014-09-13 11:55发表 [回复]
技术分享
改进后的冒泡,感觉时间复杂度没有提升很多吧!
同样是 T(n) = n*(n+(n-1)+(n-2)+(n-3)+ ...+1)
O(n^2)
博主怎么看 ?
10楼 lyg542133352 2014-09-06 11:06发表 [回复]
技术分享
二元选择排序算法写错了!
我写了一个!博主看看
selectTwoSort(int a[],int n){
int i;
for(i=0;i<=n/2;i++){ // i从0开始,不然第一个数据,就没有参与比较
int min = i;
int max = n-i-1;//最大数据指针应该这么设置吧!
int tmp = 0;
int j;
for(j= i+1;j<n-i-1;j++){
if(a[j]<a[min]){
min = j;
continue;
}
if(a[j]>a[max]){
max = j;
}
}
//放置最小值
tmp = a[min];
a[min] = a[i];
a[i] = tmp;
//放置最大值
tmp= a[max];
a[max]=a[n-i-1];
a[n-i-1]=tmp;
}
}
Re: 真实的归宿 2015-05-17 11:54发表 [回复]
技术分享
回复lyg542133352:恩,严谨。谢谢提醒
Re: 叶子美美 2014-09-16 10:12发表 [回复]
技术分享
回复lyg542133352:二元选择排序感觉还是错了,由于很难将清楚,也写了一篇博客http://blog.csdn.net/ye_scofield/article/details/39312717
Re: 焚稿 2014-10-08 12:11发表 [回复]
技术分享
回复叶子美美:的确有问题,二元选择排序没有考虑到:一次选择时,最大值恰巧在队首的情况下,会出问题。比如int a[] = {60, 41 , 59 , 33, 58 };
Re: Jack_Stan 2015-12-18 16:11发表 [回复]
技术分享
回复焚稿:void SelectSort(int a[],int n){ int min,max,temp; for (int i=0;i<(n+1)/2;i++) { min=max=i;//分别记录最大和最小关键字记录位置; for (int j=i+1;j<n-i;j++) { if (a[j]>a[max]) { max=j;continue; } if (a[j]<a[min]) { min=j; } } //该交换操作必须分情况讨论,否则可能出现首为max尾为min,不分类讨论会出错; if (max!=i) { if (min!=n-i-1) { temp=a[i];a[i]=a[min];a[min]=temp;//正常情况,分别处理min,max到首尾处; temp=a[n-i-1];a[n-i-1]=a[max];a[max]=temp; } else { temp=a[i];a[i]=a[min];a[min]=temp;//先将尾位置的min交换到首; temp=a[n-i-1];a[n-i-1]=a[max];a[max]=temp; } } else { if (min!=n-i-1) { temp=a[n-i-1];a[n-i-1]=a[max];a[max]=temp;//先将首位置的max交换到尾; temp=a[i];a[i]=a[min];a[min]=temp; } else { temp=a[min];a[min]=a[max];a[max]=temp;//首尾互换一次就够! } } }}交换时候,要分类讨论,就不会出错了,效率也高
Re: qq_26014757 2016-06-21 23:20发表 [回复]
技术分享
  1. [reply]Jack_Stan[/reply]  
  2. 我想了半天,想到一个更好的方法,不用分那么多情况讨论。代码是这样的:  
  3. package test.java;  
  4.   
  5. public class aa {  
  6.     public static void main(String[] args){  
  7.     int[] a = {10,9,9,4,1,9,5,1,1};  
  8.     SelectSort(a, a.length);  
  9.     }  
  10. public static void SelectSort(int r[], int n){   
  11.     int i ,j , min ,max, tmp;   
  12.     for (i=0 ;i <= n/2;i++) {   
  13.     // 做不超过n/2趟选择排序   
  14.     min = max = i; //分别记录最大和最小关键字记录位置   
  15.     for (j= i+1; j< n-i; j++) {    
  16.         
  17.         if (r[j] > r[max]) {   
  18.         max = j ;  
  19.         continue ;   
  20.         }   
  21.         if (r[j]< r[min]) {   
  22.         min = j ;   
  23.         }  
  24. //      System.out.println("max="+max+",min="+min);  
  25.          
  26.       }   
  27.      if(i==max){  
  28.             max=min;  
  29.             }  
  30.     //该交换操作还可分情况讨论以提高效率   
  31.     tmp = r[i]; r[i] = r[min]; r[min] = tmp;   
  32.     tmp = r[n - i -1]; r[n - i -1] = r[max]; r[max] = tmp;  
  33.     print(r); //打印每趟排序的结果   
  34.     }   
  35.     }  
  36.     public static void print(int[] a){  
  37.     for (int i : a) {  
  38.     System.out.print(i + " ");  
  39.     }  
  40.     System.out.println("");  
  41.     }  
  42.     }  
Re: qq_26014757 2016-06-21 23:19发表 [回复]
技术分享
回复Jack_Stan:我想了半天,想到一个更好的方法,不用分那么多情况讨论。代码是这样的:
package test.java;

public class aa {
public static void main(String[] args){
int[] a = {10,9,9,4,1,9,5,1,1};
SelectSort(a, a.length);
}
public static void SelectSort(int r[], int n){ 
int i ,j , min ,max, tmp; 
for (i=0 ;i <= n/2;i++) { 
// 做不超过n/2趟选择排序 
min = max = i; //分别记录最大和最小关键字记录位置 
for (j= i+1; j< n-i; j++) {

if (r[j] > r[max]) { 
max = j ;
continue ; 

if (r[j]< r[min]) { 
min = j ; 
}
// System.out.println("max="+max+",min="+min);

}
if(i==max){
max=min;
}
//该交换操作还可分情况讨论以提高效率 
tmp = r[i]; r[i] = r[min]; r[min] = tmp; 
tmp = r[n - i -1]; r[n - i -1] = r[max]; r[max] = tmp;
print(r); //打印每趟排序的结果 

}
public static void print(int[] a){
for (int i : a) {
System.out.print(i + " ");
}
System.out.println("");
}
}
Re: qq_26014757 2016-06-19 17:52发表 [回复]
技术分享
回复Jack_Stan:厉害,知道错,就是找不到原因,看了你的终于知道了。不过二元插入这么写感觉代码比原来有点复杂,不能有其他简便的写法了?
9楼 Count-MCristo 2014-08-27 17:39发表 [回复]
技术分享
受用了
8楼 AndroidArenas 2014-08-27 00:00发表 [回复]
技术分享
谢谢博主 图文并茂 获益匪浅
7楼 程晨c 2014-08-23 09:31发表 [回复]
技术分享
设置一标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。” 这个改进方法是不是有问题呢? 
“pos位置之后的记录均已交换到位”这个是错误的。
假如 100,2,200,10,20 这样的数组,第一次循环冒泡后排序为 2,100,10,20,200 最后一次的交换位置在最后的位置, 那么下次排序并不能从本次的最后一个交换的位置开始
6楼 小小攻城师 2014-07-30 20:47发表 [回复]
技术分享
太给力了,支持博主
5楼 01世界里遨游 2013-10-25 16:05发表 [回复]
技术分享
图文并茂,非常好.
4楼 hzjdandan 2013-09-12 14:16发表 [回复]
技术分享
冒泡排序这句写错了
for(int j = i; j < n-i-1; ++j) 
应该是
for(int j = 0; j < n-i-1; ++j)
Re: 真实的归宿 2013-09-12 16:53发表 [回复]
技术分享
回复hzjdandan:确实是。
3楼 yanxi_v5 2013-06-04 21:25发表 [回复]
技术分享
二元选择排序 r[0]压根就没排啊
2楼 御风天 2012-09-17 19:59发表 [回复] [引用] [举报]
技术分享
直接插入算法
while( j>=0 && x <a[ j ])
{}
Re: yzleebj 2016-04-05 16:36发表 [回复]
技术分享
回复御风天:直接插入法最好用do吧,减少一次赋值void insertSort(int a[],int N){ for(int i=1;i<N;i++) { if(a[i]<a[i-1]) { int temp=a[i]; int j=i-1; do { a[j+1]=a[j]; j--; } while(j>=0&&temp<a[j]); a[j+1]=temp; } }}
Re: xiong3110 2014-12-23 17:14发表 [回复]
技术分享
回复御风天:well done
Re: 楚之烟 2014-08-13 21:16发表 [回复] [引用] [举报]
技术分享
回复御风天:+1
Re: whisshen 2014-08-22 20:16发表 [回复] [引用] [举报]
技术分享
回复楚之烟:+1
Re: zt1006840145 2014-09-27 00:43发表 [回复] [引用] [举报]
技术分享
回复whisshen:+1

八大排序算法

标签:

原文地址:http://www.cnblogs.com/Feiyang-Lafe/p/5800304.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!