數據結構(排序二)

希爾排序數組

  這裏內部用了直接插入的方法;優化

  • 將待排序的序列按某種規則分紅幾個子序列(這個規則就是增量,增量通常選序列長度的一半,而後逐半遞減,直到最後增量爲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 }

堆排序ui

  對選擇排序的改進,每次找最小值的時候沒有利用上次的結果,因此將記錄所有存入徹底二叉樹中,構造大頂堆或小頂堆,每次取一個極值,這樣就能夠利用前一次的比較結果了spa

  • 將數據存入二叉堆(徹底二叉樹:大頂堆/小頂堆)
  • 大頂堆根結點賦值到尾部記錄(小頂堆賦頭部)【大頂堆:根結點大於兩子結點】
  • 而後將剩餘的 n-1 重新構造堆
  • 重複以上兩步,便堆序列進行了排序

徹底二叉樹的特色:code

  • 根結點爲n,左孩子爲2*n,右孩子2*n+1
  • 徹底二叉樹中有n個結點時,樹中有子子樹的結點數目爲【n/2】,且【n/2】爲最後一個有子樹的結點,

  其實也不須要構造二叉樹,須要將數組放棄0下標位置用其餘下標對應相應徹底二叉樹結點便可blog

 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的排序段用直接插入法就能夠進一步優化快速排序it

 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 }

  固然,快速排序法中有兩次遞歸調用,也是能夠利用僞遞歸的方法對其進行更深一步的優化io

相關文章
相關標籤/搜索