標籤:
0.基本概念
記錄:待排序的項目
關鍵詞:決定排序結果
穩定性:相同關鍵詞的記錄保持原來的相對次序 1.1插入排序(Insertion Sort)
演算法思想
一種簡單直觀的排序演算法,工作原理是通過構建有序序列:對於未排序資料,在已排序序列中從後向前掃描,找到相應位置並插入。
演算法描述
具體演算法描述如下:
- 從第一個元素開始,該元素可以認為已經被排序
- 取出下一個元素,在已經排序的元素序列中從後向前掃描
- 如果該元素(已排序)大於新元素,將該元素移到下一位置
- 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
- 將新元素插入到該位置後
- 重複步驟2~5
如果比較操作的代價比交換操作大的話,可以採用二分尋找法來減少比較操作的數目。該演算法可以認為是插入排序的一個變種,稱為二分尋找插入排序。
演算法分析
1.穩定的
2.最好情況:(n-1)
最好情況就是,序列已經是升序排列了,在這種情況下,需要進行的比較操作需(n-1)次即可。
最壞情況:n(n-1)/2
3.最壞情況就是,序列是降序排列,那麼此時需要進行的比較共有n(n-1)/2次:第2個元素比較1次,第3個元素比較2次…第n個元素比較n-1次,求和為n(n-1)/2。
4.平均複雜度:O(n2)
平均來說插入排序演算法複雜度為O(n2)。因而,插入排序不適合對於資料量比較大的排序應用。但是,如果需要排序的資料量很小,例如,量級小於千,那麼插入排序還是一個不錯的選擇。插入排序在工業級庫中也有著廣泛的應用,在STL的sort演算法和stdlib的qsort演算法中,都將插入排序作為快速排序的補充,用於少量元素的排序(通常為8個或以下)。 1.2希爾排序
演算法思想
也稱遞減增量排序演算法,是插入排序的一種更高效的改進版本。希爾排序是非穩定排序演算法。
1.改進依據
- 插入排序在對幾乎已經排好序的資料操作時,效率高,即可以達到線性排序的效率
- 但插入排序一般來說是低效的,因為插入排序每次只能將資料移動一位
2.演算法思想
希爾排序通過將比較的全部元素分為幾個地區來提升插入排序的效能。這樣可以讓一個元素可以一次性地朝最終位置前進一大步。然後演算法再取越來越小的步長進行排序,演算法的最後一步就是普通的插入排序,但是到了這步,需排序的資料幾乎是已排好的了(此時插入排序較快)。
一個更好理解的希爾排序實現
數組表,將數組列在一個表中並對列排序(用插入排序)。重複這過程,不過每次用更長的列來進行。最後整個表就只有一列了。將數群組轉換至表是為了更好地理解這演算法,演算法本身僅僅對原數組進行排序(通過增加索引的步長,例如是用i += step_size而不是i++)。
例如,假設有這樣一組數[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果我們以步長為5開始進行排序,我們可以通過將這列表放在有5列的表中來更好地描述演算法,這樣他們就應該看起來是這樣:
13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10
然後我們對每列進行排序:
10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45
將上述四行數字,依序接在一起時我們得到:[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].這時10已經移至正確位置了,然後再以3為步長進行排序:
10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45
排序之後變為:
10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94
最後以1步長進行排序(此時就是簡單的插入排序了)。
演算法分析
1.不穩定
2.步長與複雜度
步長的選擇是希爾排序的重要部分。只要最終步長為1任何步長序列都可以工作。演算法最開始以一定的步長進行排序。然後會繼續以一定步長進行排序,最終演算法以步長為1進行排序。當步長為1時,演算法變為插入排序,這就保證了資料一定會被排序。
Donald Shell最初建議步長選擇為
並且對步長取半直到步長達到1。雖然這樣取可以比
類的演算法(插入排序)更好,但這樣仍然有減少平均時間和最差時間的餘地。可能希爾排序最重要的地方在於當用較小步長排序後,以前用的較大步長仍然是有序的。比如,如果一個數列以步長5進行了排序然後再以步長3進行排序,那麼該數列不僅是以步長3有序,而且是以步長5有序。如果不是這樣,那麼演算法在迭代過程中會打亂以前的順序,那就不會以如此短的時間完成排序了。
已知的最好步長序列是由Sedgewick提出的(1, 5, 19, 41, 109,...),該序列的項來自
和
這兩個算式。這項研究也表明“比較在希爾排序中是最主要的操作,而不是交換。”用這樣步長序列的希爾排序比插入排序要快,甚至在小數組中比快速排序和堆排序還快,但是在涉及大量資料時希爾排序還是比快速排序慢。
另一個在大數組中表現優異的步長序列是(斐波那契數列除去0和1將剩餘的數以黃金分區比的兩倍的冪進行運算得到的數列):(1, 9, 34, 182, 836, 4025, 19001, 90358, 428481, 2034035, 9651787, 45806244, 217378076, 1031612713,…) 2.1冒泡排序(Bubble Sort)
演算法思想
不斷交換反序對:
是一種簡單的排序演算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個演算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。
演算法實現
- 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
- 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數。
- 針對所有的元素重複以上的步驟,除了最後一個。
- 持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
演算法分析
1.穩定
2.複雜度
冒泡排序對
個項目需要O(
)的比較次數,且可以原地排序。儘管這個演算法是最簡單瞭解和實現的排序演算法之一,但它對於少數元素之外的數列排序是很沒有效率的。
冒泡排序是與插入排序擁有相等的已耗用時間,但是兩種演算法在需要的交換次數卻很大地不同。在最好的情況,冒泡排序需要
次交換,而插入排序只要最多
交換。冒泡排序的實現(類似下面)通常會對已經排序好的數列拙劣地運行(
),而插入排序在這個例子只需要
個運算。因此很多現代的演算法教科書避免使用冒泡排序,而用插入排序替換之。冒泡排序如果能在內部迴圈第一次運行時,使用一個旗標來表示有無需要交換的可能,也可以把最好的複雜度降低到
。在這個情況,已經排序好的數列就無交換的需要。若在每次走訪數列時,把走訪順序反過來,也可以稍微地改進效率。有時候稱為雞尾酒排序,因為演算法會從數列的一端到另一端之間穿梭往返。
2.2快速排序 Quick Sort
演算法思想
不斷交換反序對:使用分治法(Divide and conquer)策略來把一個序列(list)分為兩個子序列(sub-lists)。
演算法實現
- 從數列中挑出一個元素,稱為"基準"(pivot),
- 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分區結束之後,該基準就處於數列的中間位置。這個稱為分區(partition)操作。
- 遞迴地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。
遞迴的最底部情形,是數列的大小是零或一,也就是永遠都已經被排序好了。雖然一直遞迴下去,但是這個演算法總會結束,因為在每次的迭代(iteration)中,它至少會把一個元素擺到它最後的位置去。
演算法分析
1.不穩定
2.複雜度
在平均狀況下,排序n個項目要Ο(n log n)次比較。在最壞狀況下則需要Ο(n2)次比較,但這種狀況並不常見。事實上,快速排序通常明顯比其他Ο(n log n)演算法更快,因為它的內部迴圈(inner loop)可以在大部分的架構上很有效率地被實現出來。
一個執行個體
3.1選擇排序(Selection sort)
演算法思想
是一種簡單直觀的排序演算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
選擇排序的主要優點與資料移動有關。如果某個元素位於正確的最終位置上,則它不會被移動。選擇排序每次交換一對元素,它們當中至少有一個將被移到其最終位置上,因此對n個元素的表進行排序總共進行至多n-1次交換。在所有的完全依靠交換去移動元素的排序方法中,選擇排序屬於非常好的一種。
演算法分析
1.不穩定
2.複雜度
擇排序的交換操作介於
和
次之間。選擇排序的比較操作為
次之間。選擇排序的賦值操作介於
和
次之間。
比較次數
,比較次數與關鍵字的初始狀態無關,總的比較次數
。交換次數
,最好情況是,已經有序,交換0次;最壞情況是,逆序,交換
次。交換次數比冒泡排序較少,由於交換所需CPU時間比比較所需的CPU時間多,
值較小時,選擇排序比冒泡排序快。
原地操作幾乎是選擇排序的唯一優點,當方度(space complexity)要求較高時,可以考慮選擇排序;實際適用的場合非常罕見。 堆排序(Heapsort)
演算法思想
是指利用堆這種資料結構所設計的一種排序演算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的索引值或索引總是小於(或者大於)它的父節點。
實質上是一個淘汰賽
演算法實現
1.堆節點的訪問
通常堆是通過一維數組來實現的。在數組起始位置為0的情形中:
- 父節點i的左子節點在位置(2*i+1);
- 父節點i的右子節點在位置(2*i+2);
- 子節點i的父節點在位置floor((i-1)/2);
2.堆的操作
在堆的資料結構中,堆中的最大值總是位於根節點。堆中定義以下幾種操作:
- 最大堆調整(Max_Heapify):將堆的末端子節點作調整,使得子節點永遠小於父節點
- 建立最大堆(Build_Max_Heap):將堆所有資料重新排序
- 堆排序(HeapSort):移除位在第一個資料的根節點,並做最大堆調整的遞迴運算
演算法分析
1.不穩定
2.複雜度
堆排序的平均時間複雜度為
,空間複雜度為
。
堆排序執行個體
歸併排序 (Merge sort)
演算法思想
歸併操作(merge),也叫歸併演算法,指的是將兩個已經排序的序列合并成一個序列的操作。歸併排序演算法依賴歸併操作。
演算法實現
1.迭代法
- 申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合并後的序列
- 設定兩個指標,最初位置分別為兩個已經排序序列的起始位置
- 比較兩個指標所指向的元素,選擇相對小的元素放入到合并空間,並移動指標到下一位置
- 重複步驟3直到某一指標到達序列尾
- 將另一序列剩下的所有元素直接複製到定序序列尾
2.遞迴法
原理如下(假設序列共有n個元素):
- 將序列每相鄰兩個數字進行歸併操作,形成
個序列,排序後每個序列包含兩個元素
- 將上述序列再次歸併,形成
個序列,每個序列包含四個元素
- 重複步驟2,直到所有元素排序完畢
演算法分析
1.穩定
2.複雜度
比較操作的次數介於
和
。 賦值操作的次數是
。歸併演算法的空間複雜度為:Θ(n)
資料結構雜談(一)淺談基本排序演算法