1. 程式人生 > 程式設計 >快速、歸併、堆排、冒泡、選擇、插入詳解

快速、歸併、堆排、冒泡、選擇、插入詳解

快速排序

分割槽(partition)

給一個陣列arr和一個num,要求大於num的在左邊,小於num的在右邊,要求時間複雜度是O(N),空間複雜度是O(1)。

虛擬碼:

1、給一個指標i,初始位置是0,表示當前指向的位置
2、進行分割槽操作,初始化 <= 區的位置在 -1
3、當前數 <= num 時,把當前數和 <= 區的下一個數交換,<= 往前走一個位置,當前數往前跳一個。
4、當前數 > num 時,直接往前跳一個。
5、重複3、4步驟,直到整個陣列遍歷完成。
複製程式碼

code

荷蘭國旗問題

現有紅白藍三個不同顏色的小球,亂序排列在一起,請重新排列這些小球,使得紅白藍三色的同顏色的球在一起。這個問題之所以叫荷蘭國旗問題,是因為我們可以將紅白藍三色小球想象成條狀物,有序排列後正好組成荷蘭國旗。

<==>其實這個問題等價於

給你一個陣列 arr和一個num。要求大於 num 在左邊,等於 num 在中間,小於 num 的在右邊。要求時間複雜度是 O(N)。空間複雜度是 O(1)

虛擬碼:

1、分割槽為 <區、>區
2、用指標 i 表示當前值,指標 less 表示 < 區的值(初始是 -1),指標 more 表示 > 區的值(初始是arr.length)
3、當前值 < num。把 i和 < 區的後一個數交換,i往前走,<區往前走
4、當前值 > num。把 i和 > 區的前一個樹交換,< 區往前走,i 不動
5、當前值 = num。i++
6、重複3、4、5直到遍歷完成
複製程式碼

code

leetcode題目地址

經典快排

主要應用了partiton的思想。

經典快排程式碼 改進後的快排

時間複雜度

O(N^2)
因為會出現最壞情況。比如 1,2,3,4,5,6,7,8,9
第一次parition。  1,9              其中partition中迴圈是9次
第二次parition 8作為哨兵 1,9          8

這是個等差數列 N + N-1 + .... + 1
(N+1)*N/2 ~ O(N^2)
所以時間複雜度是 O(N^2)

最好的時候是通過partition分割槽的值是數值的中位數
這樣通過master公式得出時間複雜度是 O(Nlog
N) T(N) = 2T(N/2) + O(N) ~ O(N) 改進方式: 在通過隨機性最後讓陣列中別的值取代,這樣時間複雜度是 O(NlogN) 綜上: 經典快排的時間複雜度是 **O(N^2)** 改進的快排時間複雜度是 **O(NlogN)** 複製程式碼

空間複雜度

O(logN)
因為在操作中要把左右位置記住,下次partition時才能從相應的位置開始,
而這些位置是的數量就是一個樹的高度,所以時間複雜度是 O(logN)。
複製程式碼

穩定性

[ 2,1]  由演演算法思想得知,在排序過程中兩個2是會互換位置的,這個演演算法是不穩定的。
複製程式碼

優化

1、在哨兵處增加隨機性,可以使得快排的時間複雜度降到O(NlogN)
2、partition 時使用荷蘭國旗方式分成三段分割槽
3、快排數量較少時,使用其他普通的排序方式(在快排的過程是使用其他排序方式)。
複製程式碼

改進後的快排

歸併排序

merge

給兩個陣列各自都是有序的,如何使得全部有序?

虛擬碼:

[2,7]   [5,9,10]

1、準備兩個指標i、j,分別指向兩個陣列的頭部。再準備一個輔助陣列help,臨時儲存排好序的部分陣列。指標 n 指向輔助陣列0位置。
2、把 i 指標指向的值和 j 指標指向的值進行比較,誰小複製誰到輔助陣列help。同時 n 和 更小的指標往前走一步。直到指標i或者指標j到陣列的末尾
3、指標i 或者 指標 j 退出迴圈。把還沒跑到指標尾部的資料複製到 help 陣列中。
4、返回 help
複製程式碼

merge

歸併排序邏輯

思想:

歸併排序是藉助上面 merge 的思想: 先把陣列分成左右兩部分,先讓左邊有序,
再讓右邊有序,最後整體有序。在 merge 中依次比較左右兩部分的值,
誰小 copy 誰到 help 陣列。然後在把 help 陣列複製到原陣列相應的位置。

複製程式碼

mergeSort

時間複雜度是 O(NlogN),因為這個排序可以表示成 T(N) = T(N/2) + O(N)。

根據 master 公式這個時間複雜度是 O(NlogN)

空間複雜度是 O(N)。因為在 merge 中有個 help 陣列申請。

下面問題是歸併排序思想的應用。

小和問題

在一個陣列中, 每一個數左邊比當前數小的數累加起來, 叫做這個陣列的小和。 求一個陣列的小和。

例子:

對於[1,5]
1左邊比1小的數, 沒有;
3左邊比3小的數, 1;
4左邊比4小的數, 1、 3;
2左邊比2小的數, 1;
5左邊比5小的數, 1、 3、 4、 2
所以小和為1+1+3+1+1+3+4+2=16
複製程式碼

xiaohe

逆序對問題

在陣列中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對,輸入一個陣列,求出這個陣列中的逆序對的總數P。

比如在陣列 [1,5] 中總共有這兩個逆序對[3,2]、[4、2] 
複製程式碼

code

逆序對問題2

與上題一樣,也是在 merge 中過程中操作

reverse-pairs

code

堆排序

堆排序

氣泡排序

氣泡排序思想程式碼

比較前一個位置的數如果比後一個大,就交換,否則不交換。
複製程式碼

code

氣泡排序複雜度分析

  1. 演演算法時間是時間複雜度為O(n²)
  2. 空間複雜度為O(1)
  3. 選擇排序是穩定的(如果有相等元素,不交換,就可以做到穩定)

選擇排序

選擇排序思想程式碼

1、從待排序序列中,找到關鍵字最小的元素;起始假定第一個元素為最小
2、如果最小元素不是待排序序列的第一個元素,將其和第一個元素互換
3、從餘下的 N - 1 個元素中,找出關鍵字最小的元素,重複1,2步,直到排序結束。
複製程式碼

code

選擇排序複雜度分析

  1. 演演算法時間是時間複雜度為O(n²)
  2. 空間複雜度為O(1)
  3. 選擇排序是不穩定的( 不穩定演演算法,比如陣列 [5,1],依次排序過後,1,5交換位置。兩個5的相對次序發生了變化,因此是不穩定性的)

插入排序

插入排序思想程式碼

1、從第一個元素開始,該元素可以認為已經被排序
2、取出下一個元素,在已經排序的元素序列中從後向前掃描
3、如果下一個元素比掃描的元素小,交換。否則,跳出迴圈,繼續下次迴圈的排序
複製程式碼

code

插入排序複雜度分析

  1. 時間是時間複雜度為O(n²)。
  2. 空間複雜度為O(1)
  3. 插入排序是穩定的(對於陣列[2,4],當4插入有序陣列2,4,6時,當相等時,就停止,不做處理,可以做到穩定性)

總結

排序方法 時間複雜度 空間複雜度 穩定性
快排 O(NlogN)