九大排序算法

http://www.cnblogs.com/xwdreamer/archive/2012/04/06/2435123.html

http://www.cnblogs.com/biyeymyhjob/archive/2012/07/17/2591457.htmlphp

 

http://www.cnblogs.com/codingmylife/archive/2012/10/21/2732980.htmlhtml


 

冒泡排序:最好狀況需比較n-1次,最壞狀況需比較n(n-1)/2;
選擇排序:最好狀況需比較n(n-1)/2,最壞狀況需比較n(n-1)/2;
對分排序:最好狀況需比較n/2logn,最壞狀況需比較近似nlogn;

 

1.排序算法簡要比較

  冒泡排序java

名稱 數據對象 穩定性 時間複雜度 空間複雜度 描述
平均 最壞
插入排序 數組、鏈表 O(n^2) O(1) (有序區,無序區)。把無序區的第一個元素插入到有序區的合適的位置。對數組:比較得少,換得多。
直接選擇排序 數組 × O(n^2) O(1) (有序區,無序區)。在無序區裏找一個最小的元素跟在有序區的後面。 對數組:比較得多,換得少。
鏈表
堆排序 數組 × O(nlogn) O(1) (最大堆,有序區)。從堆頂把根卸出來放在有序區以前,再恢復堆。
歸併排序 數組、鏈表 O(nlogn) O(n) +O(logn) , 若是不是從下到上 把數據分爲兩段,從兩段中逐個選最小的元素移入新數據段的末尾。可從上到下或從下到上進行。
快速排序 數組 × O(nlogn) O(n^2) O(logn) ,O(n) (小數,樞紐元,大數)。
Accum qsort 鏈表 O(nlogn) O(n^2) O(logn) ,O(n) (無序區,有序區)。把無序區分爲(小數,樞紐元,大數),從後到前壓入有序區。
   
決策樹排序   O(logn!) O(n!) O(n) <O(logn!) <O(nlogn)
   
計數排序 數組、鏈表 O(n) O(n+m) 統計小於等於該元素值的元素的個數 i,因而該元素就放在目標數組的索引 i位。(i≥0)
桶排序 數組、鏈表 O(n) O(m) 將值爲 i 的元素放入i 號桶,最後依次把桶裏的元素倒出來。
基數排序 數組、鏈表     一種多關鍵字的排序算法,可用桶排序實現。
  • 均按從小到大排列
  • n 表明數據規模
  • m 表明數據的最大值減最小值

2.冒泡排序:

冒泡排序基本思路:

  冒泡排序的主要思想就是經過一趟排序,將數組中的最大值轉移到的數組最後一個元素。一個有n個元素的數組,那麼通過(n-1)趟排序之後,就可以完成排序。由於若是(n-1)個元素的位置肯定了,那麼最後的那一個元素的位置也就肯定了。還有須要注意的是每一趟排序都能肯定數組中的一個元素位置,所以通過一趟排序之後,須要進行排序比較的元素就減小一個。c++

  在每一趟排序過程當中,都是從數組元素第一個開始,依次比較相鄰的兩個數,將小數放在前面,大數放在後面。即在第一趟:首先比較第1個和第2個數,將小數放前,大數放後。而後比較第2個數和第3個數,將小數放前,大數放後,如此繼續,直至比較最後兩個數,將小數放前,大數放後。至此第一趟結束,將最大的數放到了最後。在第二趟:仍從第一對數開始比較(由於可能因爲第2個數和第3個數的交換,使得第1個數再也不小於第2個數),將小數放前,大數放後,一直比較到倒數第二個數(倒數第一的位置上已是最大的),第二趟結束,在倒數第二的位置上獲得一個新的最大數(其實在整個數列中是第二大的數)。如此下去,重複以上過程,直至最終完成排序。因爲在排序過程當中老是小數往前放,大數日後放,至關於氣泡往上升,因此稱做冒泡排序。算法

冒泡排序的改進

冒泡排序法存在的不足及改進方法:windows

  第一,在排序過程當中,執行完當前的第k趟排序後,可能數據已所有排序完備,可是程序沒法判斷是否完成排序,會繼續執行剩下的(n-1-k)趟排序。爲了解決這一不足,能夠設置一個標誌位flag,將其初始值設置爲非0,表示被排序的數組是一個無序的數組。每一次排序開始前設置flag值爲0,表示假設已經完成排序,若是這一趟排序過程當中都沒有數據發生交換,則flag的值就爲0;可是當這一趟排序過程當中存在數據交換時,修改flag爲非0,表示這一趟仍然有數據沒有完成排序。在新一輪排序開始時,檢查此標誌,若此標誌爲0,表示上一次沒有作過交換數據,則表示通過上一趟的排序,已經完成了對數組的排序,排序結束;不然繼續進行排序;數組

根據上述思路進行改進的冒泡排序代碼實現以下:ide

View Code

   第二,當排序的數據比較多時排序的時間會明顯延長。改進方法:快速排序:具體作法:任意選取某一記錄(一般取第一個記錄),比較其關鍵字與全部記錄的關鍵字,並將關鍵字比它小的記錄所有放在它的前面,將比它大的記錄均存放在它的後面,這樣,通過一次排序以後,可將全部記錄以該記錄所在的分界點分爲兩部分,而後分別對這兩部分進行快速排序,直至排序完。也就是說,利用快速排序過程當中的Partition()方法返回的index將須要排序的元素分爲兩段,在index前面的數據都比index位置的數據小,在index後面的元素都比index位置的數據大。而後對先後兩段數據進行冒泡排序,這樣縮小的冒泡排序的數據量。函數

  局部冒泡排序算法對冒泡排序的改進:性能

  在冒泡排序中,一趟掃描有可能無數據交換,也有可能有一次或屢次數據交換,在傳統的冒泡排序算法及近年來的一些改進的算法中,只記錄一趟掃描有無數據交換的信息,對數據交換髮生的位置信息則不予處理。爲了充分利用這一信息,能夠在一趟全局掃描中,對每一反序數據對進行局部冒泡排序處理,稱之爲局部冒泡排序。局部冒泡排序與冒泡排序算法具備相同的時間複雜度,而且在正序和逆序的狀況下,所需的關鍵字的比較次數和移動次數徹底相同。因爲局部冒泡排序和冒泡排序的數據移動次數老是相同的,而局部冒泡排序所需關鍵字的比較次數常少於冒泡排序,這意味着局部冒泡排序極可能在平均比較次數上對冒泡排序有所改進,當比較次數較少的優勢不足以抵消其程序複雜度所帶來的額外開銷,而當數據量較大時,局部冒泡排序的時間性能則明顯優於冒泡排序。

3.快速排序

 參考前面寫的博文:

快速排序算法QuickSort

快速排序算法QuickSort(二)

冒泡排序與快速排序代碼實現

View Code

4.堆排序

堆排序的主要思路:

  1. 通常狀況下都使用數組實現堆的結構,以大頂堆爲例,由於是使用數組,元素下表從0開始,因此要求arry[i]>arry[i*2+1],arry[i]>arry[i*2+2]。表示父結點大於其左右孩子結點的值。
  2. 因此堆排序的第一步是根據其實數組元素,構建一個堆。構建堆就須要調整堆中的元素,從第一個非葉子結點開始調整,第一個非葉子結點的位置是t=(len/2-1)。由於堆是一棵滿二叉樹的結構,因此從0到t的全部結點都是非葉子結點。
  3. 在調整堆結構過程當中,咱們須要比較父節點與其左右子結點的值,能夠先求出左右子結點的最大值,即c=max(arry[leftChild],arry[rightChild]),而後將這個最大值與父節點arry[parent]進行比較,若是父節點小於子節點,那麼交換父節點與子節點的值(子節點中較大的那個元素),若是父節點大與子節點,則代表知足堆結構要求,不須要交換。在調整的過程當中可能會出現一種狀況,好比父節點A與子節點B進行交換,此時A結點是B結點的子節點,可是A結點又是CD兩個結點的父節點,所以咱們還須要調整ACD三個元素位置,一直調整到結點沒有子節點爲止。
  4. 在堆排序中,首先是構建一個最大堆,此處須要用到buildHeap(int arry[],int len)這個函數,而這個函數中又調用了adjustHeap(int arry[],int parent,int len),因此能夠將build當作是由adjustheap組成的。在構建完成之後,咱們交換首元素與末元素的位置,交換之後咱們知道首元素不符合最大堆的定義,因此須要調整首元素的位置,也就是adjustHeap(arry,0,len-i-1)。
  5. 咱們能夠發現adjustHeap(int arry[],int parent,int len)的時間複雜讀是o(logn),而須要調用o(n)次這個方法啊,因此時間複雜度時o(nlogn)

堆排序的代碼實現

c++實現

View Code

java實現(ps:2012-10-8)

View Code

此處使用異或方法交換兩個元素的值存在一些bug,建議仍是使用常規的交換元素方法。

5.歸併排序

參考文獻:白話經典算法系列之五 歸併排序的實現

5.1歸併排序思路

  1. 歸併排序是創建在歸併操做上的一種有效的排序算法。該算法是採用分治法(Divide and Conquer)的一個很是典型的應用。分治的思想相似於快速排序,不過它比快速排序多了一步merge。首先考慮下如何將將二個有序數組A/B合併的問題。這個很是簡單,設定兩個指針分別指向A和B的首地址,而後比較這兩個指針所指向的數,哪個數小就將其保存到臨時數組T中,而後指針往前移動一個位置。好比指針pa指向數組A的首地址,pb指向數組B的首地址,指針pt指向臨時數組的首地址。加入pa指向的數小於pb指向的數,則將pa所指向的數存放在pt所指的位置上,而後pa和pt都往前移動一格。重複上述操做,直到pa和pb有一個指向數組的末尾元素。此時將另一個指針沒有指向末尾元素的數組中的元素所有都保存到臨時數組中。這樣就完成了兩個有序數組的歸併操做。
  2. 解決了上面的合併有序數列問題,再來看歸併排序,其的基本思路就是將一個數組分紅二組,分爲A和B,若是這二組組內的數據都是有序的,那麼就能夠很方便的將這二組數據進行歸併。那麼如何讓這二組組內數據有序了?能夠將A,B組各自再分紅二組。依次類推,當分出來的小組只有一個數據時,能夠認爲這個小組組內已經達到了有序,而後再合併相鄰的二個小組就能夠了。這樣經過先遞歸的分解數列,再合併數列就完成了歸併排序。

5.2代碼示例

View Code

注意點:

在這裏咱們看到咱們merge的分段是arry[start...mid]跟arry[mid+1...end],咱們不能分紅arry[start...mid-1]跟arry[mid...end],這是由於mid=(start+end)/2,假如start=1,end=2,則mid=1,此時調用MergeSort(arry,start=1,end=3,temp);會就變成了MergeSort(arry,mid=1,end=3,temp)的死循環了。

相關文章
相關標籤/搜索