圖解排序算法

一  插入排序

1.1  直接插入排序

基本思想:每次將一個待排序額記錄按其關鍵碼的大小插入到一個已經排好序的有序序列中,直到全部記錄排好序。

圖解:


代碼實現:
  1. //直接順序排序  
  2. void InsertSort(int r[], int n)  
  3. {     
  4.     for (int i=2; i<n; i++)  
  5.     {   
  6.       r[0]=r[i];                        //設置哨兵  
  7.       for (int j=i-1; r[0]<r[j]; j--)   //尋找插入位置  
  8.             r[j+1]=r[j];                //記錄後移  
  9.       r[j+1]=r[0];                   
  10.     }  
  11.     for(int k=1;k<n;k++)  
  12.        cout<<r[k]<<" ";     
  13.     cout<<"\n";  
  14. }  


1.2 希爾排序

基本思想是: 先將整個待排序記錄序列分割成若干個子序列,在在序列內分別進行直接插入排序,待整個序列基本有序時,再對全體記錄進行一次直接插入排序。

圖解:

代碼實現:
  1. <span style="font-size:14px;">//希爾排序  
  2. void ShellSort(int r[], int n)  
  3. {     
  4.     int i;  
  5.     int d;  
  6.     int j;  
  7.     for (d=n/2; d>=1; d=d/2)            //以增量爲d進行直接插入排序  
  8.     {  
  9.          for (i=d+1; i<n; i++)     
  10.          {     
  11.              r[0]=r[i];                 //暫存被插入記錄  
  12.                for (j=i-d; j>0 && r[0]<r[j]; j=j-d)  
  13.                      r[j+d]=r[j];       //記錄後移d個位置  
  14.                           r[j+d]=r[0];  
  15.          }  
  16.     }  
  17.    for(i=1;i<n;i++)  
  18.        cout<<r[i]<<" ";  
  19.    cout<<"\n";  
  20. }</span>  



二 交換排序

2.1 起泡排序

起泡排序是交換排序中最簡單的排序方法,其基本思想是: 兩兩比較相鄰記錄的關鍵碼,如果反序則交換,直到沒有反序的記錄爲止。

圖解:



代碼實現:
  1. <span style="font-size:14px;">//起泡排序  
  2. void BubbleSort(int r[], int n)  
  3. {  
  4.     int temp;  
  5.     int exchange;  
  6.     int bound;  
  7.     exchange=n-1;                       //第一趟起泡排序的範圍是r[0]到r[n-1]      
  8.     while (exchange)                    //僅當上一趟排序有記錄交換才進行本趟排序  
  9.     {  
  10.         bound=exchange;   
  11.         exchange=0;    
  12.         for (int j=0; j<bound; j++)     //一趟起泡排序  
  13.         if (r[j]>r[j+1])   
  14.         {  
  15.           temp=r[j];  
  16.           r[j]=r[j+1];  
  17.           r[j+1]=temp;  
  18.           exchange=j;                   //記錄每一次發生記錄交換的位置  
  19.        }  
  20.     }  
  21.     for(int i=0;i<n;i++)  
  22.        cout<<r[i]<<" ";  
  23.     cout<<"\n";  
  24. }</span>  


2.2快速排序

基本思想:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。

圖解:


代碼實現:
  1. //快速排序一次劃分  
  2. int Partition(int r[], int first, int end)  
  3. {     
  4.     int i=first;                        //初始化  
  5.     int j=end;  
  6.     int temp;          
  7.   
  8.     while (i<j)    
  9.     {    
  10.        while (i<j && r[i]<= r[j])  
  11.             j--;                        //右側掃描  
  12.        if (i<j)  
  13.        {   
  14.              temp=r[i];                 //將較小記錄交換到前面  
  15.              r[i]=r[j];  
  16.              r[j]=temp;  
  17.               i++;   
  18.        }  
  19.        while (i<j && r[i]<= r[j])   
  20.            i++;                         //左側掃描  
  21.            if (i<j)  
  22.            {  
  23.               temp=r[j];  
  24.               r[j]=r[i];  
  25.               r[i]=temp;                //將較大記錄交換到後面  
  26.                j--;   
  27.            }  
  28.     }  
  29.     return i;                           //i爲軸值記錄的最終位置  
  30. }  
  31.   
  32. //快速排序  
  33. void QuickSort(int r[], int first, int end)  
  34. {  
  35.     if (first<end)   
  36.     {                                   //遞歸結束  
  37.            int pivot=Partition(r, first, end);  //一次劃分  
  38.            QuickSort(r, first, pivot-1);//遞歸地對左側子序列進行快速排序  
  39.            QuickSort(r, pivot+1, end);  //遞歸地對右側子序列進行快速排序  
  40.     }  
  41.   
  42. }  



三 選擇排序

3.1 簡單選擇排序

基本思想:設所排序序列的記錄個數爲n。i取1,2,…,n-1,從所有n-i+1個記錄(Ri,Ri+1,…,Rn)中找出排序碼最小的記錄,與第i個記錄交換。執行n-1趟 後就完成了記錄序列的排序。

圖解:

代碼實現:
  1. //簡單選擇排序  
  2. void SelectSort(int r[ ], int n)  
  3. {   
  4.     int i;  
  5.     int j;  
  6.     int index;  
  7.     int temp;  
  8.     for (i=0; i<n-1; i++)                //對n個記錄進行n-1趟簡單選擇排序  
  9.     {    
  10.        index=i;           
  11.        for (j=i+1; j<n; j++)            //在無序區中選取最小記錄  
  12.          if (r[j]<r[index])  
  13.              index=j;  
  14.        if (index!=i)   
  15.        {  
  16.           temp=r[i];  
  17.           r[i]=r[index];  
  18.           r[index]=temp;  
  19.        }  
  20.     }  
  21.     for(i=0;i<n;i++)  
  22.         cout<<r[i]<<" ";  
  23.     cout<<"\n";  
  24. }  


3.2 堆排序

堆的定義

    是具有下列性質的完全二叉樹:每個結點的值都小於或等於其左右孩子結點的值(小根堆);或者每個結點的值都大於或等於其左右孩子結點的值(大根堆)。

大根堆和小根堆:根結點(亦稱爲堆頂)的關鍵字是堆裏所有結點關鍵字中最小者的堆稱爲小根堆,又稱最小堆。根結點(亦稱爲堆頂)的關鍵字是堆裏所有結點關鍵字中最大者,稱爲大根堆,又稱最大堆。注意:①堆中任一子樹亦是堆。②以上討論的堆實際上是二叉堆(Binary Heap),類似地可定義k叉堆。


假設當前要篩選結點的編號爲k,堆中最後一個結點的編號爲m,並且結點k的左右子樹均是堆(即r[k+1] ~ r[m]滿足堆的條件),則篩選算法用僞代碼可描述爲:


具體的篩選代碼如下:

  1. //篩選法調整堆  
  2. void Sift(int r[], int k, int m)  
  3. {  
  4.     
  5.     int i;  
  6.     int j;  
  7.     int temp;  
  8.     i=k;   
  9.     j=2*i+1;                            //置i爲要篩的結點,j爲i的左孩子  
  10.   while (j<=m)                          //篩選還沒有進行到葉子  
  11.   {  
  12.       if (j<m && r[j]<r[j+1])   
  13.           j++;                          //比較i的左右孩子,j爲較大者  
  14.       if (r[i]>r[j]) break;             //根結點已經大於左右孩子中的較大者  
  15.       else   
  16.       {  
  17.            temp=r[i];  
  18.            r[i]=r[j];  
  19.            r[j]=temp;                   //將根結點與結點j交換  
  20.            i=j;  
  21.            j=2*i+1;                     //被篩結點位於原來結點j的位置  
  22.      }  
  23.   }  
  24. }  


堆排序

堆排序的基本思想是:首先將待排序的記錄序列構造成一個堆,此時,選出了堆中所有記錄的最大者即堆頂記錄,然後將它從堆中移走(通常將堆頂記錄和堆中最後一個記錄交換),並將剩餘的記錄再調整成堆,這樣又找出了次大的記錄,以此類推,直到堆中只有一個記錄爲止。


(1)用大根堆排序的基本思想
① 先將初始文件R[1..n]建成一個大根堆,此堆爲初始的無序區
② 再將關鍵字最大的記錄R[1](即堆頂)和無序區的最後一個記錄R[n]交換,由此得到新的無序區R[1..n-1]和有序區R[n],且滿足R[1..n-1].keys≤R[n].key
③由於交換後新的根R[1]可能違反堆性質,故應將當前無序區R[1..n-1]調整爲堆。然後再次將R[1..n-1]中關鍵字最大的記錄R[1]和該區間的最後一個記錄R[n-1]交換,由此得到新的無序區R[1..n-2]和有序區R[n-1..n],且仍滿足關係R[1..n-2].keys≤R[n-1..n].keys,同樣要將R[1..n-2]調整爲堆。
……

直到無序區只有一個元素爲止。

(2)大根堆排序算法的基本操作:

① 初始化操作:將R[1..n]構造爲初始堆;

② 每一趟排序的基本操作:將當前無序區的堆頂記錄R[1]和該區間的最後一個記錄交換,然後將新的無序區調整爲堆(亦稱重建堆)。

注意:

①只需做n-1趟排序,選出較大的n-1個關鍵字即可以使得文件遞增有序。

②用小根堆排序與利用大根堆類似,只不過其排序結果是遞減有序的。堆排序和直接選擇排序相反:在任何時刻堆排序中無序區總是在有序區之前,且有序區是在原向量的尾部由後往前逐步擴大至整個向量爲止







代碼實現:
  1. //堆排序  
  2. void HeapSort(int r[ ], int n)  
  3. {  
  4.      
  5.   int i;  
  6.   int temp;  
  7.   for (i=n/2; i>=0; i--)                //初始建堆,從最後一個非終端結點至根結點  
  8.      Sift(r, i, n) ;       
  9.    for (i=n-1; i>0; i--)                //重複執行移走堆頂及重建堆的操作  
  10.    {  
  11.        temp=r[i];  
  12.        r[i]=r[0];  
  13.        r[0]=temp;  
  14.       Sift(r, 0, i-1);  
  15.    }  
  16.    for(i=0;i<n;i++)  
  17.       cout<<r[i]<<" ";  
  18.    cout<<"\n";  
  19. }  


四 歸併排序

二路歸併排序
基本思想:將若干個有序序列進行兩兩歸併,直至所有待排序記錄都在一個有序序列爲止。



一路歸併算法實現:
  1. //一次歸併  
  2. void Merge(int r[], int r1[], int s, int m, int t)  
  3. {  
  4.   
  5.     int i=s;  
  6.     int j=m+1;  
  7.     int k=s;  
  8.         
  9.      while (i<=m && j<=t)  
  10.      {     
  11.          if (r[i]<=r[j])   
  12.              r1[k++]=r[i++];            //取r[i]和r[j]中較小者放入r1[k]  
  13.          else   
  14.              r1[k++]=r[j++];   
  15.      }  
  16.       if (i<=m)   
  17.           while (i<=m)                  //若第一個子序列沒處理完,則進行收尾處理           
  18.               r1[k++]=r[i++];   
  19.       else    
  20.           while (j<=t)                  //若第二個子序列沒處理完,則進行收尾處理          
  21.             r1[k++]=r[j++];   
  22. }  



  1. //一趟歸併  
  2. void MergePass(int r[ ], int r1[ ], int n, int h)  
  3. {  
  4.     int i=0;  
  5.     int k;  
  6.   
  7.    while (i<=n-2*h)                     //待歸併記錄至少有兩個長度爲h的子序列  
  8.    {  
  9.      Merge(r, r1, i, i+h-1, i+2*h-1);  
  10.         i+=2*h;  
  11.    }  
  12.    if (i<n-h)   
  13.        Merge(r, r1, i, i+h-1, n);       //待歸併序列中有一個長度小於h  
  14.    else for (k=i; k<=n; k++)            //待歸併序列中只剩一個子序列  
  15.         r1[k]=r[k];  
  16. }  
  17.   
  18. //歸併排序的非遞歸算法  
  19. void MergeSort1(int r[ ], int r1[ ], int n )  
  20. {   
  21.   int h=1;  
  22.   int i;  
  23.   
  24.   while (h<n)  
  25.   {  
  26.     MergePass(r, r1, n-1, h);           //歸併  
  27.     h=2*h;  
  28.     MergePass(r1, r, n-1, h);  
  29.     h=2*h;  
  30.   }  
  31.   for(i=0;i<n;i++)  
  32.       cout<<r[i]<<" ";  
  33.   cout<<"\n";  
  34. }  


下面看看二路歸併排序的遞歸實現


  1. //歸併排序的遞歸算法  
  2. void MergeSort2(int r[], int r1[], int r2[],int s, int t)  
  3. {   
  4.    
  5.     int m;  
  6.     if (s==t)   
  7.     {  
  8.         r1[s]=r[s];  
  9.   
  10.     }  
  11.     else   
  12.     {   
  13.             m=(s+t)/2;  
  14.             MergeSort2(r, r2, r1, s, m);        //歸併排序前半個子序列          
  15.             MergeSort2(r, r2, r1, m+1, t);      //歸併排序後半個子序列  
  16.             Merge(r2, r1, s, m, t);             //將兩個已排序的子序列歸併        
  17.     }      
  18. }  



總結

各種排序方法的比較(未完待續):




發佈了0 篇原創文章 · 獲贊 14 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章