[數據結構複習]八大內排序算法總結

======來自N多資料,主要參考http://blog.csdn.net/hguisu/article/details/7776068 以及 http://blog.csdn.net/onedreamer/article/details/6745006 ===============

一、穩定性和時間複雜度總結

這裏寫圖片描述
當n較大,則應採用時間複雜度爲O(nlog2n)的排序方法:快速排序、堆排序或歸併排序序。
快速排序是目前基於比較的內部排序中被認爲是最好的方法,當待排序的關鍵字是隨機分佈時,快速排序的平均時間最短;

(1)平方階(O(n2))排序
  各類簡單排序:直接插入、直接選擇和冒泡排序;
(2)線性對數階(O(nlog2n))排序
  快速排序、堆排序和歸併排序;
(3)O(n1+§))排序,§是介於0和1之間的常數。
希爾排序
(4)線性階(O(n))排序
  基數排序,此外還有桶、箱排序。
說明:
當原表有序或基本有序時,直接插入排序和冒泡排序將大大減少比較次數和移動記錄的次數,時間複雜度可降至O(n);
而快速排序則相反,當原表基本有序時,將蛻化爲冒泡排序,時間複雜度提高爲O(n2);
原表是否有序,對簡單選擇排序、堆排序、歸併排序和基數排序的時間複雜度影響不大。

二、排序算法思想總結

下面按照上圖的順序總結它們的排序思想。

1.直接插入

將一個記錄插入到已排序好的有序表中,從而得到一個新的,記錄數增1的有序表。如果碰見一個和插入元素相等的,那麼插入元素把想插入的元素放在相等元素的後面。所以,相等元素的前後順序沒有改變,從原無序序列出去的順序就是排好序後的順序,所以插入排序是穩定的。
其他的插入排序有二分插入排序,2-路插入排序。
二分很容易理解:在插入法中添加了二分查找的意思。不贅述。
2-路:它的目的就是想減少數據的移動次數,因此,另外開闢輔助空間。首先開闢一個長度爲n的臨時數組,將待排序數組的第1個元素放到臨時數組的第0位,作爲初始化。將該值作爲每次排序的參照,大於等於這個參照值就後插,小於參照值就前插。同時定義兩個遊標first和final分別指向臨時數組當前最小值和最大值所在位置。如下圖所示:
這裏寫圖片描述

2.shell排序

相對直接排序有較大的改進。希爾排序又叫縮小增量排序。先將整個待排序的記錄序列分割成爲若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行依次直接插入排序。
操作方法:
選擇一個增量序列t1,t2,…,tk,其中ti遞減,tk=1;
按增量序列個數k,對序列進行k 趟排序;
每趟排序,根據對應的增量ti,將待排序列分割成若干長度爲m 的子序列,分別對各子表進行直接插入排序。僅增量因子爲1 時,整個序列作爲一個表來處理,表長度即爲整個序列的長度。
我們簡單處理增量序列:增量序列d = {n/2 ,n/4, n/8 …..1} n爲要排序數的個數
即:先將要排序的一組記錄按某個增量d(n/2,n爲要排序數的個數)分成若干組子序列,每組中記錄的下標相差d.對每組中全部元素進行直接插入排序,然後再用一個較小的增量(d/2)對它進行分組,在每組中再進行直接插入排序。繼續不斷縮小增量直至爲1,最後使用直接插入排序完成排序。
示例:

3.直接選擇

在要排序的一組數中,選出最小(或者最大)的一個數與第1個位置的數交換;然後在剩下的數當中再找最小(或者最大)的與第2個位置的數交換,依次類推,直到第n-1個元素(倒數第二個數)和第n個元素(最後一個數)比較爲止。
對直接選擇排序的一種改進是:
一次循環找出最大以及最小的數,分別放在前面和後面。這樣可以減少一半的循環次數。

4.堆排序

堆對應一棵完全二叉樹
建堆方法:對初始序列建堆的過程,就是一個反覆進行篩選的過程。
1)n 個結點的完全二叉樹,則最後一個結點是第這裏寫圖片描述個結點的子樹。
2)篩選從第這裏寫圖片描述個結點爲根的子樹開始,該子樹成爲堆。(怎麼做:從當前結點開始,與它的左右子女中的最大的比,如果比最大的子女大,就交換位置,繼續向下比較直到無子女。)
3)之後向前依次對各結點爲根的子樹進行篩選,使之成爲堆,直到根結點。
如圖建堆初始過程:無序序列:(49,38,65,97,76,13,27,49)
這裏寫圖片描述
貼段代碼助理解

void HeapAdjust(int H[],int s, int length)   {       int tmp  = H[s];       int child = 2*s+1; //左孩子結點的位置。(child+1 爲當前調整結點的右孩子結點的位置)       while (child < length) {           if(child+1 <length && H[child]<H[child+1]) { // 如果右孩子大於左孩子(找到比當前待調整結點大的孩子結點)               ++child ;           }           if(H[s]<H[child]) {  // 如果較大的子結點大於父結點               H[s] = H[child]; // 那麼把較大的子結點往上移動,替換它的父結點               s = child;       // 重新設置s ,即待調整的下一個結點的位置               child = 2*s+1;           }  else {            // 如果當前待調整結點大於它的左右孩子,則不需要調整,直接退出                break;           }           H[s] = tmp;         // 當前待調整的結點放到比其大的孩子結點位置上       }       print(H,length);   }     /**   * 初始堆進行調整   * 將H[0..length-1]建成堆   * 調整完之後第一個元素是序列的最小的元素   */   void BuildingHeap(int H[], int length)   {        //最後一個有孩子的節點的位置 i=  (length -1) / 2       for (int i = (length -1) / 2 ; i >= 0; --i)           HeapAdjust(H,i,length);   }  

**輸出堆頂元素後,對剩餘n-1元素重新建成堆的調整過程。
調整小頂堆的方法:**
1)設有m 個元素的堆,輸出堆頂元素後,剩下m-1 個元素。將堆底元素送入堆頂((最後一個元素與堆頂進行交換),堆被破壞,其原因僅是根結點不滿足堆的性質。
2)將根結點與左、右子樹中較小元素的進行交換。
3)若與左子樹交換:如果左子樹堆被破壞,即左子樹的根結點不滿足堆的性質,則重複方法 (2).
4)若與右子樹交換,如果右子樹堆被破壞,即右子樹的根結點不滿足堆的性質。則重複方法 (2).
5)繼續對不滿足堆性質的子樹進行上述交換操作,直到葉子結點,堆被建成。
稱這個自根結點到葉子結點的調整過程爲篩選。如圖:
這裏寫圖片描述
最後的排序算法也是利用上面的HeapAdjust方法:

/**   * 堆排序算法   */   void HeapSort(int H[],int length)   {       //初始堆       BuildingHeap(H, length);       //從最後一個元素開始對序列進行調整       for (int i = length - 1; i > 0; --i)       {           //交換堆頂元素H[0]和堆中最後一個元素           int temp = H[i]; H[i] = H[0]; H[0] = temp;           //每次交換堆頂元素和堆中最後一個元素之後,都要對堆進行調整           HeapAdjust(H,0,i);     }   }   

5.冒泡排序

在要排序的一組數中,對當前還未排好序的範圍內的全部數,自上而下對相鄰的兩個數依次進行比較和調整,讓較大的數往下沉,較小的往上冒。即:每當兩相鄰的數比較後發現它們的排序與排序要求相反時,就將它們互換。
改進:
加個變量記錄本次遍歷是否有交換。如果沒有,停止循環。

6.快速排序

要記得是遞歸的!
1)選擇一個基準元素,通常選擇第一個元素或者最後一個元素,
2)通過一趟排序講待排序的記錄分割成獨立的兩部分,其中一部分記錄的元素值均比基準元素值小。另一部分記錄的 元素值比基準值大。
3)此時基準元素在其排好序後的正確位置
4)然後分別對這兩部分記錄用同樣的方法繼續進行排序,直到整個序列有序。
示例:
(a)一趟排序的過程:
這裏寫圖片描述
(b)排序的全過程
這裏寫圖片描述
快排的一種改進:
只對長度大於k的子序列遞歸調用快速排序,讓原序列基本有序,然後再對整個基本有序序列插入排序算法排序。實踐證明,改進後的算法時間複雜度有所降低,且當k取值爲 8 左右時,改進算法的性能最佳。

7.歸併排序

歸併(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]

8.基數排序

先看下桶排序:

三、具體代碼實現

看着上面的思路自己寫。
留待日後補充。

1.直接插入

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章