排序是程式設計中的一種重要操作,它的功能是將乙個資料元素(或記錄)的任意序列,重新排列成乙個關鍵字有序的序列。
對序列中存在的任意的兩個相等的資料元素a和b,如果a在b之前,在用某個排序演算法排序後,a仍然在b之前,則就稱該排序演算法是穩定的;否則,就稱該排序演算法是不穩定的。
不穩定排序演算法可能會更改相等資料元素的相對次序,但是穩定排序演算法從來不會更改相等資料元素的相對次序。不穩定排序演算法可以被特別地實現為穩定。
資料元素是否相等是和資料元素相關的,如果是語言支援的原子型別就是數值大小,如果是複雜的資料結構就按照該結構定義的比較方法進行比較。
衡量排序演算法的好壞通常從以下方面考慮:
時間複雜度
空間複雜度
穩定性
常用的排序演算法包括:氣泡排序,選擇排序,插入排序,希爾排序,快速排序,堆排序,歸併排序
氣泡排序使用的基本方法是依次比較相鄰的兩個數,將小數放在前面,大數放在後面(或者大數放前邊,小數放後邊,這樣實現的是元素遞減排序)。
按照這樣的方法,氣泡排序在第一趟依次比較第1個和第2個元素,第2個和第3個...,第n-1個和第n個,執行完後最大值(最小值)就到了第n個位置。
然後繼續下一趟,這一趟比較的元素數目比上一趟少乙個,因為在上一趟中由有乙個元素找到了自己在排序好的序列中的位置。重複執行這一步直到整個序列都被排序。
氣泡排序的本質思想在於:遍歷要排序的序列,並依次比較相鄰的兩個元素,如果它們的相對次序違反了排序的要求,就互換其位置;該演算法將一直重複這個過程直到它的某一次遍歷過程中沒有發現任何需要交換位置的元素。
時間複雜度:如需要排序的序列是已經排好序的,則該演算法只需要遍歷一遍序列,在遍歷過程中只需進行n-1次比較,且不移動任何元素,此時其時間複雜度最好為o(n);如果要排序的序列的初始狀態為"逆序",則需進行n(n-1)/2次比較和移動。此時其時間複雜度最差,為o(n^2)。
空間複雜度: 氣泡排序排序過程中只需要乙個元素的空間用來完成元素交換
穩定性:氣泡排序是穩定的
選擇排序的思想很簡單:每一趟從待排序的序列中選出最小(或最大)的乙個元素,順序放在已排好序的序列的合適位置(如果已排序序列放在整個序列的尾部,就是最前;如果已排序序列放在整個序列的頭部,就是最後),直到全部待排序的資料元素排完。簡單舉例,要求對序列進行遞增排序
1. 假設已排序序列放在整個序列頭部,且某一趟排序完成後序列狀態為,則下一趟選出的最小值9應該放在已排序序列的最後即3,4的後邊
2. 假設已排序序列放在整個序列頭部,且某一趟排序完成後序列狀態為,則下一趟選出的最大值50應該放在已排序序列的最前即51,70的前邊
對比選擇排序和氣泡排序,氣泡排序只要發現任何違反排序要求的元素序列,就交換它們的位置,而選擇排序則比較懶惰,它發現乙個比當前選擇的元素大的元素時只記錄它的位置,並在遍歷過程中不斷更新所選取元素的位置,只在本趟遍歷結束時才進行一次元素的交換,因此在通常情況下選擇排序的懶惰使得它具有比氣泡排序更好的效能。為什麼只是通常呢,注意到氣泡排序可能存在「短路」的可能即提前退出的可能,它如果在一趟遍歷過程中沒有進行任何元素的位置交換就可以認為已經完成了排序,但是選擇排序則無法提前退出,因而假如序列是已經排好序的冒牌排序的效能就優於選擇排序。
從選擇排序和氣泡排序的對比中可以得到一點啟示:延遲費時的操作(比如這裡的交換元素位置)有時候是可以提高效能的。
時間複雜度:選擇排序的交換操作次數介於0 和 (n - 1) 次之間,最好情況是序列已經有序,交換0次;最壞情況是序列逆序,交換n-1次。選擇排序的比較次數固定為 n (n - 1) / 2,比較次數與序列的初始狀態無關。因此其時間複雜度為為o(n^2)。
空間複雜度:選擇排序排序過程中只需要乙個元素的空間用來完成元素交換
穩定性:選擇排序是不穩定的
插入排序的基本思想:
把要排序的序列分成有序部分和無序部分兩部分,有序部分包含了序列中的第乙個元素(或者最後乙個元素)
依次將無序部分的每乙個元素插入到有序部分中去
實際上只要進行排序,序列都可以看作由有序部分和無序部分兩部分組成。但與氣泡排序和選擇排序不同,氣泡排序和選擇排序主要針對的是無序部分,其關鍵點在於從無序部分中找出其中的最大值(或最小值),然後再將其放入有序部分;而插入排序則主要針對的是有序部分,順序從無序部分中取出每乙個元素,然後將其插入到有序部分中去。就是這一點的改變就對效能有了很大的影響,因為有序部分是有規律的,因而插入時可以在找到合適位置時就退出,而不必一定遍歷整個有序部分,這就節省了不少時間。
從比較中可以得到一點啟示:針對有規律的資料進行設計很可能可以提高軟體效率,降低複雜度。
時間複雜度: 如需要排序的序列是已經排好序的,則該演算法只需要遍歷一遍序列,在遍歷過程中只需進行n-1次比較,且不移動任何元素,此時其時間複雜度最好為o(n)。如果要排序的序列的初始狀態為"逆序",則需進行n(n-1)/2次比較和移動。此時其時間複雜度最差,為o(n^2)。
空間複雜度: 插入排序排序過程中只需要乙個元素的空間用來完成元素交換
穩定性: 插入排序是穩定的
雖然時間複雜度都是o(n^2),但是插入排序的效能是好於氣泡排序和選擇排序的。
希爾排序實際上是插入排序的一種高階版本。
其基本思想:
先取乙個小於n的整數step1作為第乙個步長,把檔案的全部記錄分成step1個組,其中所有距離為step1的倍數的記錄放在同乙個組中,各個組的起始元素分別為。
在各組上使用插入排序將每個組都排序
取第二個步長step2
該方法實質上是一種分組插入方法。它充分利用了插入排序對有序的序列效率很高的特性,通過不斷將序列分組排序使得序列逐步變得有序,最終在整個序列上使用插入排序。在剛開始排序時元素很無序,步長最大,所以插入排序的元素個數很少,速度很快;當元素基本有序了,步長也變小了。
步長的選擇是希爾排序的重要部分。只要最終步長為1任何步長序列都可以工作。好的步長序列的共同特徵:
已知的最好步長序列由marcin ciura設計(1,4,10,23,57,132,301,701,1750,…),也有說是sedgewick提出的 (1, 5, 19, 41, 109,...)
時間複雜度:希爾排序的時間複雜度取決於所使用的序列,序列最差時複雜度為o(n^2),序列最優時為 o(n (logn )^2)
空間複雜度:希爾排序排序過程中只需要乙個元素的空間用來完成元素交換
穩定性:希爾排序是不穩定的
它的基本思想是將要排序的序列劃分成兩部分,其中一部分的所有元素都比另外一部分的任意元素都要小,然後再按此方法對這兩部分分別進行快速排序,整個排序過程遞迴進行直到完成排序。
快速排序的關鍵點在於劃分序列為兩部分,它是分析快速排序效能的關鍵也是實現快速排序演算法時的關鍵。一種快速排序的劃分方法:
設定兩個變數i、j,排序開始的時候:i=0,j=n-1;
以第乙個陣列元素作為關鍵資料,賦值給key,即 key=a[0];
從j開始向前搜尋,即由後開始向前搜尋(j--),直到j等於i或者找到乙個小於key的值a[j],如果找到了乙個小於key的a[j],就將其值賦給a[i];
從i開始向後搜尋,即由前開始向後搜尋(i++),直到i等於j或者找到乙個大於key的值a[i],如果找到了乙個大於key的a[i],就將其值賦給a[j];
如果i不等於j,就回到步驟2;
將key賦給a[i]
快速排序是目前已知的常用排序演算法中最快的排序方法。
時間複雜度:快速排序的最壞情況發生在序列已經有序時,此時其時間複雜度為o(n^2)。為了防止這種情況發生可以在選擇劃分主元時採用其它的方法,比如採用隨機選取的某個元素。快速排序的平均情況和最好情況的時間複雜度都為o(nlogn)。
空間複雜度:快速排序在排序過程中需要乙個元素的空間來輔助完成元素交換
穩定性:快速排序是不穩定的
堆排序利用了堆這一資料結構。
堆:n個關鍵字序列kl,k2,…,kn稱為堆當且僅當該序列滿足如下性質:ki<=k(2i)且 ki<=k(2i+1)(1≤i≤ n)(這是小頂堆)或者ki>=k(2i)且 ki>=k(2i+1)(1≤i≤ n)(這是大頂堆),其中k(i)相當於二叉樹的非葉結點,k(2i)則是左孩子,k(2i+1)是右孩子
若將此序列所儲存的向量r[1..n]看做是一棵完全二叉樹的儲存結構,則堆實質上是滿足如下性質的完全二叉樹:樹中任一非葉結點的關鍵字均不大於(或不小於)其左右孩子(若存在)結點的關鍵字。
堆排序的方法:
將初始序列建成乙個大頂堆(或小頂堆),此時得到了兩個子串行,乙個是不包含任何元素的有序子串行,乙個是包含了所有元素的無序子串行,並且無序子串行是乙個大頂堆(小頂堆)
將堆頂和無序序列的最後乙個元素交換,由此得到新的少了乙個元素的無序序列,同時乙個新的元素被加入到了有序序列中
如果無序區的元素數目大於1,就調整無序區使其成為乙個大頂堆(小頂堆),並回到步驟2;否則無序區只有乙個元素,排序完成
可以看出堆排序實際上也是一種選擇排序。與普通的選擇排序相比,它實際上是通過堆這樣的資料結構儲存了排序過程中的中間結果,即如果兩個元素已經做過比較,就將其相對大小通過堆來儲存起來,這就可以減少比較次數,從而提高了效能。這種思想實際上是利用了電腦科學中的乙個最基本的手段:快取,利用快取來快取一些中間結果,在大多數情況下都可以提高軟體的效率。
)時間複雜度:堆排序的時間主要用於調整堆的次序上,其最壞情況和平均情況的時間複雜度都為o(nlogn)
空間複雜度:堆排序在排序過程中需要乙個元素的空間來輔助完成元素交換
穩定性:堆排序不穩定
歸併排序的基本思想是將兩個或兩個以上的有序序列合併成乙個新的有序序列。歸併排序可以有兩種方式來實現:
1.自頂向下
如果待排序序列只有乙個元素,則排序完成退出;否則將該序列從中間劃分稱兩個子串行
分別對兩個子串行進行排序
合併兩個排好序的子串行得到乙個新的有序序列
2.自下向上
將序列看成有n個包含乙個元素的子串行組成的序列,此時子串行長度為1
如果子串行長度大於等於n,則排序完成,退出
依次合併相鄰的兩個子串行,即子串行1和子串行2合併,子串行3和子串行4合併,一次類推
將子串行的長度乘以2,並回到步驟2
時間複雜度:歸併排序的時間複雜度為o(nlogn)
空間複雜度:歸併排序需要額外的n個輔助空間來輔助完成排序
穩定性:歸併排序是穩定的排序演算法
常用排序演算法
筆者最近學習演算法,學了很久也只弄懂了幾個排序演算法,在這裡曬一下下,作為以後參考之用。一 為什麼要研究排序問題 許多計算機科學家認為,排序演算法是演算法學習中最基本的問題,原因有以下幾點 l有時候應用程式本身需要對資訊進行排序,如為了準備客戶賬目,銀行需要對支票賬號進行排序 l很多演算法將排序作為...
常用排序演算法
一 簡單排序演算法 由於程式比較簡單,所以沒有加什麼注釋。所有的程式都給出了完整的執行 並在我的vc環境 下執行通過。因為沒有涉及mfc和windows的內容,所以在borland c 的平台上應該也不會有什麼 問題的。在 的後面給出了執行過程示意,希望對理解有幫助。1.冒泡法 這是最原始,也是眾所...
常用排序演算法
排序演算法 最好時間 平均時間 最壞時間 輔助空間 穩定性 直接插入排序 o n o n 2 o n 2 o 1 穩定 希爾排序 o n 1.3 o 1 不穩定 直接選擇排序 o n 2 o n 2 o n 2 o 1 不穩定 堆排序 o n x lbn o n x lbn o n x lbn o ...