排序演算法總結與C代碼__排序演算法

來源:互聯網
上載者:User

        最近參加筆試,感覺排序演算法需要好好的整理一下,感覺部分排序演算法理解得不是很清楚;通過這段時間的整理與總結來對排序演算法的一個複習吧。

        主要參考了《大話資料結構》:
1. 冒泡排序的基本思想:兩兩比較相鄰記錄的關鍵字,如果反序則交換,直到沒有反序的記錄為止。

#define length 10void swap(int *a,int *b){int temp;temp=*a;*a=*b;*b=temp;}/*採用冒泡排序,是資料從小到大排列*/void BubbleSort0(int a[],int len)  {int i,j;for (i=0;i<len;i++){for (j=i+1;j<len;j++){if (a[i]>a[j])swap(&a[i],&a[j]);}}}
冒泡演算法的改進1,主要講相鄰兩個數比較

void BubbleSort1(int a[],int len){int i,j;for (i=0;i<len;i++){for(j=len-2;j>=i;j--){if (a[j]>a[j+1])    //前者與後者比較,這裡與前面演算法的區別swap(&a[j],&a[j+1]);}}}
冒泡演算法的進一步最佳化,使用一個標誌位來記錄是否有資料的交換,如果資料有交換,則將標誌flag賦值為true

void BubbleSort2(int a[],int len){int i,j;int flag=1;for (i=0;i<len &&flag;i++){flag=0;for (j=len-2;j>=i;j--){if (a[j]>a[j+1]){swap(&a[j],&a[j+1]);flag=1;}}}}
冒泡排序複雜度分析:當最好的情況(序列已經有序),根據最後的改進的代碼,只有n-1次的比較,沒有資料之間的交換,時間複雜度為O(n);當最壞的情況下(序列為逆序),此時需要比較n(n-1)/2,並且還需要進行等數量級的記錄移動,因此總的時間複雜度為O(n^2)
2.簡單選擇排序

簡單選擇排序:就是通過n-i次關鍵字間的比較,從n-i+1個記錄中選出關鍵字最小的記錄,並和第i個記錄交換:

void SelectSort(int a[],int len){int i,j,min;for (i=0;i<len;i++){min=i;for (j=i+1;j<len;j++){if (a[min]>a[j])min=j;}if (i!=min)swap(&a[min],&a[i]);}}
簡單選擇排序複雜度分析:它的比較次數一定:n(n-1)/2。也因此無論在序列何種情況下,它都不會有優秀的表現,可見對資料的有序性不敏感。它雖然比較次數多,而對於交換次數而言,當最好的時候,交換為0次,最壞的時候交換次數為n-1次,所以資料交換量卻很少,基於時間複雜度是比較與交換次數的總和,因此總的時間複雜度依然為O(n^2)。儘管與冒泡排序演算法的時間複雜度相同,但是簡單選擇排序的效能上還是略優於冒泡排序。
3.直接插入排序

直接插入排序的基本操作是將一個記錄插入到已經排好序的有序表中,從而得到一個新的記錄數增1的有序表

void InsertSort(int a[],int len){int i,j,temp;for (i=1;i<len;i++){if (a[i]<a[i-1]){temp=a[i];for (j=i-1;a[j]>temp;j--)a[j+1]=a[j];a[j+1]=temp;}}}
直接插入排序複雜度分析:每次比較後最多移掉一個逆序,因此與冒泡排序的效率相同。但它在速度上還是要高點,這是因為在冒泡排序下是進行值交換,而在插入排序下是值移動,所以直接插入排序將要優於冒泡排序。直接插入法也是一種對資料的有序性非常敏感的一種演算法。在有序情況下只需要經過n-1次比較,由於每次都是a[j]>a[j-1]因此沒有移動記錄,時間複雜度為O(n);在最壞情況下,將需要(n+2)(n-1)/2次比較,並且將移動次數也達到最大值(n+4)(n-1)/2。如果排序記錄是隨機的,根據機率相同原則,平均比較和移動次數約為你(n^2)/4,所以直接插入排序的時間複雜度為O(n^2).

4.希爾排序

希爾排序的基本思想:先將整個待排記錄序列分割成為若干子序列分別進行直接插入排序,並使整個序列中的記錄基本有序,再對全體記錄進行一次排序。

/*主要利用插入排序的思想*/void ShellSort(int a[],int len){int i,j,temp,increment;increment=len;do{increment=increment/3+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) {a[j+increment]=a[j];}a[j+increment]=temp;}}}while(increment>1);}


希爾排序複雜度分析:希爾排序的“增量”的選取非常關鍵,大量的研究表示,當增量序列為2^(t-k+1)-1(0=<k=<t=<log2(n+1))時,可以獲得不錯的效果,其時間複雜度為O(n^1.5)。增量的選擇將影響希爾排序的效率。但是無論怎樣選擇增量,最後一定要使增量為1,進行一次直接插入排序。但它相對於直接插入排序,由於在子表中每進行一次比較,就可能移去整個經性表中的多個逆序,從而改善了整個排序效能。希爾排序算是一種基於插入排序的演算法,所以對資料有序敏感。

5.堆排序

堆排序的思想:首先將待排序列構建為大頂堆(大頂堆:每個結點的值大於或等於其左右孩子結點的值)。此時整個序列的最大值就是堆頂的根結點。將它移走(其實就是講其與堆數組的末尾元素交換,此時末尾元素就是最大值),然後將剩餘的n-1個子序列重新構造成一個堆,這樣就會得到n個元素中的次大值。如此反覆執行,便得到一個有序序列。

void HeapAdjust(int a[],int s,int m){int temp,j=0;temp=a[s];for (j=2*s+1;j<m;j=j*2+1)   {if (j<m && a[j]<a[j+1])++j;if (j<m && temp>=a[j])break;a[s]=a[j];s=j;}a[s]=temp;}

void HeapSort(int a[],int len){int i;for (i=(len)/2-1;i>=0;i--)HeapAdjust(a,i,len);for (i=len-1;i>=1;i--){swap(&a[0],&a[i]);HeapAdjust(a,0,i-1);}}


堆排序的複雜度分析:在構建堆的過程中,因為是完全二叉樹從最下層最後邊的非終端結點開始構建,將它與其孩子進行比較和若有必要的交換,對於每個非終端結點來說,其中最多進行兩次比較和互換操作,因此整個構建堆的時間複雜度為O(n)。

    在正式排序時,第i次取堆頂記錄重建堆需要用O(logi)的時間(完全二叉樹的某個結點到根節點的距離為[logi]+1),並且需要取n-1次堆頂記錄,因此重建堆的時間複雜度為O(nlogn)。

    所以總體來說,堆排序的時間複雜度為O(nlogn)。由於堆排序對原始記錄的排序狀態並不敏感,因此無論最好、最壞平均時間複雜度均為O(nlogn)。所以一般在小規模的序列中不合適,但對於較大的序列,將表現出優越的效能。

6.歸併排序
歸併排序思想:假設初始序列含有n個記錄,則可以看成是n個有序的子序列,每個子序列的長度的長度為1,然而兩兩歸併,得到[n/2]([x]表示不小於x的最小整數)個長度為2或1的有序子序列;再兩兩歸併,.....,如此重複,直至得到一個長度為n的有序序列為止。

/*將有序的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++)  /*對1...m與m+1...n中等長的數組比較大小,並且儲存較小元素*/{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){int m=0;int TR2[length+1]={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);}}

歸併排序複雜度分析:一趟歸併需要將SR[0]~SR[n-1]中相鄰的長度為h的有序序列進行兩兩歸併。並將結果放到TR[0]~TR[n-1]中,這需要將待排序列中的記錄掃描一遍,因此耗費O(n)時間,而由完全二叉樹的深度可知,整個歸併排序需要進行[log(n)]次,因 此,總的時間複雜度為O(nlogn),而且這是歸併排序演算法中最好、最壞、平均的時間效能。並且歸併排序是一種穩定的排序演算法。對資料的有序性不敏感。若資料節點資料量大,那將不適合。但可改造成索引操作,效果將非常出色。

對于歸並排序的非遞迴實現在此就不在說明。

7.快速排序

快速排序的思想:通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序的目的。

int Partiton(int a[],int low,int high){int pivotkey;pivotkey=a[low];//樞軸的選取可以選取中間值while (low<high){while (low<high &&a[high]>=pivotkey)high--;swap(&a[high],&a[low]);while (low<high && a[low]<=pivotkey)low++;swap(&a[high],&a[low]);}return low;}int Partiton1(int a[],int low,int high)/*y最佳化不需要的交換*/{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=Partiton1(a,low,high);Qsort(a,low,pivot-1);Qsort(a,pivot+1,high);}}
快速排序複雜度分析: 它同樣是冒泡排序的改進,它通過一次交換能消除多個逆序,這樣可以減少逆序時所消耗的掃描和資料交換次數。在最優情況下,它的排序時間複雜度為O(nlog2n)。即每次劃分序列時,能均勻分成兩個子串。但最差情況下它的時間複雜度將是O(n^2)。即每次劃分子串時,一串為空白,另一串為m-1(如果程式中採用每次取序列中部資料作為劃分點,那將在正序和逆時達到最優)。有的書上這解釋“快速排序”,在理論上講,如果每次能均勻劃分序列,它將是最快的排序演算法,因此稱它作快速排序。雖然很難均勻劃分序列, 但就平均效能而言,它仍是基於關鍵字比較的內部排序演算法中速度最快者。

按平均時間將排序分為類: 
(1) 平方階(O(n2))排序
  各類簡單排序,例如直接插入、簡單選擇和冒泡排序; 
(2) 線性對數階(O(nlog2n))排序
  如快速排序、堆排序和歸併排序; 
(3) O(n1+§))排序
  §是介於0和1之間的常數。希爾排序便是一種;

排序方法的選擇

因為不同的排序方法適應不同的應用環境和要求,所以選擇合適的排序方法很重要
(1)若n較小,可採用直接插入或直接選擇排序。
當記錄規模較小時,直接插入排序較好,它會比選擇更少的比較次數;
但當記錄規模較大時,因為直接選擇移動的記錄數少於直接插人,所以宜用選直接選擇排序。
這兩種都是穩定排序演算法。
(2)若檔案初始狀態基本有序(指正序),則應選用直接插人、冒泡或隨機的快速排序為宜(這裡的隨機是指基準取值的隨機,原因見上的快速排序分析);這裡快速排序演算法將不穩定。
(3)若n較大,則應採用時間複雜度為O(nlog2n)的排序方法:快速排序、堆排序或歸併排序序。
快速排序是目前基於比較的內部排序中被認為是最好的方法,當待排序的關鍵字是隨機分布時,快速排序的平均時間最短;
堆排序雖不會出現快速排序可能出現的最壞情況。但它需要建堆的過程。這兩種排序都是不穩定的。
  歸併排序是穩定的排序演算法,但它有一定數量的資料移動,所以我們可能過與插入排序組合,先獲得一定長度的序列,然後再合并,在效率上將有所提高。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.