1. 程式人生 > >數據結構與算法系列研究九——排序算法的一些探討

數據結構與算法系列研究九——排序算法的一些探討

停止 不同 位置 集合 完全 設置 img com 去除

四種排序

一.實驗內容
輸入20個整數,分別用希爾排序、快速排序、堆排序和歸並排序實現由小到大排序並輸出排序結果。
二.關鍵數據結構與核心算法
關鍵數據結構:
由於是排序為了簡單起見,選用線性表中的數組作為存儲結構。
核心算法:
1.希爾排序
希爾排序的核心還是直接插入法,但是插入的位置有所講究。要把數組分為許多段,每一段的長度除了最後的有可能不同之外,其他的都相同。該段的長度即為增量,在最後一次必須為一,此時程序變成了直接插入。每次進行隔段插入,不斷地調整是的數組變得隔段有序。這樣做的目的是為了對直接插入的改進,減小時間復雜度。

具體代碼如下:

技術分享
/*shell sort 主要利用插入排序的思想
*/ void ShellSort(int a[],int len) { int i,j,temp,increment; //增量 increment=len; //初始化為總長度 do { increment=increment/3+1; //設置每次的增量值,最後一次必為1 for (i=increment;i<len;i++) { //從增量開始遞增,若滿足後面的比前面的小,就要插入 if(a[i]<a[i-increment]) { temp
=a[i]; //臨時存儲處 for (j=i-increment;j>=0&&a[j]>temp;j-=increment) { //註意插入條件j>=0,a[j]>temp a[j+increment]=a[j]; //找到插入位置進行插入 } a[j+increment]=temp; //插入到要插的地方 } } }while(increment>1
); //註意當increment為1時仍在循環 }
View Code

2.快速排序
快速排序在最好的情況下時間復雜度最低,但是若本來數組就是序排列的,就會退化為冒泡排序。快速排序關鍵的就是一次排序的操作。
首先,選取數組的第一個元素作為支點,將此支點保存,此後該支點位置可以被覆蓋。 每次排序時,選用兩個指針,一個指向尾部,一個指向首部,如果首部小於尾部則進行循環,尾部指針一直向前移動直至首部等於尾部或者尾部所指節點小於支點,此時,若首部尾部相等循環結束,否則,將該尾部節點覆蓋到首部節點位置(註意:開始時首節點即為支點,可以覆蓋)。完成該操作後,首節點指針開始向後移動直至遇到1.首位節點相等或2.首節點所指節點大於支點,若為1,則循環結束,否則將該首指針所指節點覆蓋到上次的為節點空出的位置上,繼續進行尾部指針移動。重復上述過程有限此後,必將得到首指針等於尾指針的情況,此時將支點放到首尾指針所指節點位置。必然得到在支點左邊的元素都小於等於支點,在支點右邊的都大於等於該支點。然後返回該支點的位置,一次循環結束。
然後主函數中采用遞歸的方法即可。
具體代碼如下:

技術分享
/**************快速排序*****************/
int Partiton(int a[],int low,int high)
{  
    int temp,pivotkey;  
    pivotkey=a[low]; //選取支點
    temp=pivotkey;  //暫存支點
    while (low<high)  //條件,相等則結束
    {  
        while (low<high &&a[high]>=pivotkey)  
        {
            high--;//左移  
        }
        a[low]=a[high];//覆蓋  
        while (low<high && a[low]<=pivotkey)  
        {
            low++;  //右移
        }
        a[high]=a[low];//覆蓋  
    }  
    a[low]=temp; //恢復
    return low;  //返回分界點位置
}  
void Qsort(int a[],int low,int high)  
{  
    int pivot;  
    if (low<high)  
    {  
        pivot=Partiton(a,low,high); //獲得分界點位置
        Qsort(a,low,pivot-1); //排左邊
        Qsort(a,pivot+1,high);//右邊  
    }  
}  
/******************** end  qsort *******************/
View Code

3.堆排序
堆排序是一個比較有意思的排序,有虛擬二叉樹的結構來進行排序,簡稱大根堆。要完成堆排序,首先要建立大根堆。首先要按照層次遍歷的方法產生完全二叉樹。然後,對該二叉樹進行調整使得遞歸的來說,每個根節點都大於等於左右兒子節點。然後,首根節點即為最大元素,將該節點和最後的節點交換。交換之後,最後的節點成為了最大節點,該節點在以後的操作中認為不在大根堆中,然後調整剩余節點使之成為新的大根堆,再交換新的大根堆的最後元素和首根結點。剔除該大根堆最後節點。如此循環下去,直至大根堆只剩一個元素,此元素即為最小元素和自身交換仍為最小元素。至此數組就變成了升序排列的了。
具體代碼:

技術分享
/**********將從i開始的為頭節點的子樹調整為大根堆************/
void HAdjust(int R[], int i, int n)
{ //i為出發結點下標,n為堆的結點總數
    int  iL,iR,j;//左右子樹
    while(i<=n/2-1) //葉子停止,非葉子繼續
   {
       iL=2*i+1; //左兒子,下標從0開始
       iR=iL+1;  //右兒子
       j=iL; //j為R[i]要交換的結點下標
       if(iR<n&&R[iR]>R[iL])
       {
           j=iR;//若右子樹存在,且該節點大於左兒子,選擇右兒子

       }
       if(R[i]>R[j]) //頭節點最大
       {
           break; //無需交換,則結束
       }
       swap(R[i], R[j]);//否則交換
       i=j;//標記新的待檢核節點,看是否要交換
   }
}
void HeapSort(int R[], int n)
{ //初始建大根堆
    int i;
     for(i=n/2-1; i>=0; i--)
     {
         HAdjust(R, i, n);//建立大根堆,每次都調整使每個子樹為大根堆
     }
     for(i=1; i<n; i++)
     {
         swap(R[0], R[n-i]);//將n-i的元素和頭節點最大值交換
         HAdjust(R, 0, n-i);//去除交換後的n-i元素,將其他的節點調整為大根堆
     }//從而得到從小到大的序列
}
/***************************heap    end**************************************/
View Code

4.歸並排序
歸並排序的思想是借用一個輔助數組存儲每次歸並後的節點,每次循環中都從開始進行二路歸並,將兩個相鄰的元素歸為有序集合,每次都在縮小歸並後的集合數直至最後只剩一個集合即為升序排列的數組。

具體代碼:

技術分享
/***************歸並排序:將有序的SR歸並到TR中********************/  
void Merge(int SR[],int TR[],int i,int m,int n)  
{  
    int j,k,l;  
    for (j=m+1,k=i;i<=m && j<=n;k++)  
    {  
        if (SR[i]<SR[j])
        {
            TR[k]=SR[i++];//若前半部分小則直接加入
        }
        else   
        {
            TR[k]=SR[j++]; //否則加入後半部分
        }
    }
    //結束時可能有兩種情況
    if (i<=m)  
    {  /*將SR[1...m]中多余中的部分添加到TR中*/  
        for (l=0;l<=m-i;l++)  
        {
            TR[k+l]=SR[i+l]; //添加多余部分
        }
    }  
    if (j<=n)   
    { /*將SR[m+1...n]中多余中的部分添加到TR中*/    
        for (l=0;l<=n-j;l++)  
        {
            TR[k+l]=SR[j+l];   //添加多余部分
        }
    }  
}  
/*將SR歸並排序為TR1*/  
void MSort(int SR[],int TR1[],int s,int t)  
{  //歸並完成後t數組即為升序數組
    int m=0;  
    int TR2[100]={0};  
    if (s==t)  
    {
        TR1[s]=SR[s];//相等則復制
    }
    else  
    {  
        m=(s+t)/2;  //找到中心位置
        MSort(SR,TR2,s,m); //分別進行歸並復制,左邊
        MSort(SR,TR2,m+1,t); //右邊
        Merge(TR2,TR1,s,m,t); //歸並
    }  
}  
/******************end  merge********************/
View Code

四.理論與測試
理論:選用相同的無序數組分別進行四種排序,將得到相同的結果。
測試:無序數組為:
a[20]={12,23,43,35,37,26,19,20,1,10,
32,27,29,89,94,93,108,246,245,17};
運行程序後結果為:
技術分享

五.附錄(源代碼)

  1 #include "stdio.h"
  2 #include "stdlib.h"
  3 #include "iostream"
  4 using namespace std;
  5 /*shell sort 主要利用插入排序的思想*/
  6 void ShellSort(int a[],int len)  
  7 {  
  8     int i,j,temp,increment; //增量
  9     increment=len;          //初始化為總長度
 10     do  
 11     {  
 12         increment=increment/3+1;  //設置每次的增量值,最後一次必為1
 13         for (i=increment;i<len;i++)  
 14         {  //從增量開始遞增,若滿足後面的比前面的小,就要插入
 15             if(a[i]<a[i-increment])  
 16             {  
 17                 temp=a[i];  //臨時存儲處
 18                 for (j=i-increment;j>=0&&a[j]>temp;j-=increment)   
 19                 {  //註意插入條件j>=0,a[j]>temp
 20                     a[j+increment]=a[j];  //找到插入位置進行插入
 21                 }  
 22                 a[j+increment]=temp;  //插入到要插的地方
 23             }  
 24         }  
 25     }while(increment>1); //註意當increment為1時仍在循環
 26 }  
 27 /****************heap  sort******************/
 28 void swap(int &a,int &b)
 29 {   //交換a與b的值
 30     int temp;
 31     temp =  a;
 32     a = b;
 33     b = temp;
 34 }
 35 /**********將從i開始的為頭節點的子樹調整為大根堆************/
 36 void HAdjust(int R[], int i, int n)
 37 { //i為出發結點下標,n為堆的結點總數
 38     int  iL,iR,j;//左右子樹
 39     while(i<=n/2-1) //葉子停止,非葉子繼續
 40    {
 41        iL=2*i+1; //左兒子,下標從0開始
 42        iR=iL+1;  //右兒子
 43        j=iL; //j為R[i]要交換的結點下標
 44        if(iR<n&&R[iR]>R[iL])
 45        {
 46            j=iR;//若右子樹存在,且該節點大於左兒子,選擇右兒子
 47 
 48        }
 49        if(R[i]>R[j]) //頭節點最大
 50        {
 51            break; //無需交換,則結束
 52        }
 53        swap(R[i], R[j]);//否則交換
 54        i=j;//標記新的待檢核節點,看是否要交換
 55    }
 56 }
 57 void HeapSort(int R[], int n)
 58 { //初始建大根堆
 59     int i;
 60      for(i=n/2-1; i>=0; i--)
 61      {
 62          HAdjust(R, i, n);//建立大根堆,每次都調整使每個子樹為大根堆
 63      }
 64      for(i=1; i<n; i++)
 65      {
 66          swap(R[0], R[n-i]);//將n-i的元素和頭節點最大值交換
 67          HAdjust(R, 0, n-i);//去除交換後的n-i元素,將其他的節點調整為大根堆
 68      }//從而得到從小到大的序列
 69 }
 70 /***************************heap    end**************************************/
 71 
 72 
 73 /***************歸並排序:將有序的SR歸並到TR中********************/  
 74 void Merge(int SR[],int TR[],int i,int m,int n)  
 75 {  
 76     int j,k,l;  
 77     for (j=m+1,k=i;i<=m && j<=n;k++)  
 78     {  
 79         if (SR[i]<SR[j])
 80         {
 81             TR[k]=SR[i++];//若前半部分小則直接加入
 82         }
 83         else   
 84         {
 85             TR[k]=SR[j++]; //否則加入後半部分
 86         }
 87     }
 88     //結束時可能有兩種情況
 89     if (i<=m)  
 90     {  /*將SR[1...m]中多余中的部分添加到TR中*/  
 91         for (l=0;l<=m-i;l++)  
 92         {
 93             TR[k+l]=SR[i+l]; //添加多余部分
 94         }
 95     }  
 96     if (j<=n)   
 97     { /*將SR[m+1...n]中多余中的部分添加到TR中*/    
 98         for (l=0;l<=n-j;l++)  
 99         {
100             TR[k+l]=SR[j+l];   //添加多余部分
101         }
102     }  
103 }  
104 /*將SR歸並排序為TR1*/  
105 void MSort(int SR[],int TR1[],int s,int t)  
106 {  //歸並完成後t數組即為升序數組
107     int m=0;  
108     int TR2[100]={0};  
109     if (s==t)  
110     {
111         TR1[s]=SR[s];//相等則復制
112     }
113     else  
114     {  
115         m=(s+t)/2;  //找到中心位置
116         MSort(SR,TR2,s,m); //分別進行歸並復制,左邊
117         MSort(SR,TR2,m+1,t); //右邊
118         Merge(TR2,TR1,s,m,t); //歸並
119     }  
120 }  
121 /******************end  merge********************/
122 
123 /**************快速排序*****************/
124 int Partiton(int a[],int low,int high)
125 {  
126     int temp,pivotkey;  
127     pivotkey=a[low]; //選取支點
128     temp=pivotkey;  //暫存支點
129     while (low<high)  //條件,相等則結束
130     {  
131         while (low<high &&a[high]>=pivotkey)  
132         {
133             high--;//左移  
134         }
135         a[low]=a[high];//覆蓋  
136         while (low<high && a[low]<=pivotkey)  
137         {
138             low++;  //右移
139         }
140         a[high]=a[low];//覆蓋  
141     }  
142     a[low]=temp; //恢復
143     return low;  //返回分界點位置
144 }  
145 void Qsort(int a[],int low,int high)  
146 {  
147     int pivot;  
148     if (low<high)  
149     {  
150         pivot=Partiton(a,low,high); //獲得分界點位置
151         Qsort(a,low,pivot-1); //排左邊
152         Qsort(a,pivot+1,high);//右邊  
153     }  
154 }  
155 /******************** end  qsort *******************/
156 void MainMenu()
157 {//四個相同的數組,四種不同的排序
158     int a[20]={12,23,43,35,37,26,19,20,1,10,
159                32,27,29,89,94,93,108,246,245,17};
160     int b[20]={12,23,43,35,37,26,19,20,1,10,
161                 32,27,29,89,94,93,108,246,245,17};
162     int c[20]={12,23,43,35,37,26,19,20,1,10,
163                32,27,29,89,94,93,108,246,245,17};
164     int d[20]={12,23,43,35,37,26,19,20,1,10,
165                32,27,29,89,94,93,108,246,245,17};
166     int t[100],n=20,i;//t數組為歸並排序服務
167     printf("shell sort:\n");
168     ShellSort(a,n);
169     for(i=0;i<n;i++)
170     {
171         printf("%d  ", a[i]);
172         if((i+1)%10==0)
173         {
174             printf("\n");
175         }
176     }
177     printf("\nheap  sort:\n");
178     HeapSort(b,n);
179     for(i=0;i<n;i++)
180     {
181         printf("%d  ", b[i]);
182         if((i+1)%10==0)
183         {
184             printf("\n");
185         }
186     }
187     printf("\nmerge sort:\n");
188     MSort(c,t,0,n-1);
189     for(i=0;i<n;i++)
190     {
191         printf("%d  ", t[i]);
192         if((i+1)%10==0)
193         {
194             printf("\n");
195         }
196     }
197     printf("\nquick  sort:\n");
198     Qsort(d,0,n-1);  
199     for(i=0;i<n;i++)
200     {
201         printf("%d  ", d[i]);
202         if((i+1)%10==0)
203         {
204             printf("\n");
205         }
206     }
207     printf("\n");
208 }
209 
210 int main()
211 {
212     MainMenu();
213     return 0;
214 }

數據結構與算法系列研究九——排序算法的一些探討