基本概念
1. 時間復雜度
- 定義:一個算法流程中,常數操作數量的指標,這個指標叫做O,big O。具體為,如果常數操作數量的表達式中,只要高階項,不要低階項,也不要高階項系數之后,剩下的部分記為f(N),那么該算法的時間復雜度為O(f(N))
- 常數操作與數據量沒有關系,時間復雜度和數據量有關,只要高階項,且不要系數。
2. 空間復雜度
- 定義:給的數組不占額外空間,額外空間是為了完成排序還需要多少輔助的空間。
- 有限的幾個變量是O(1),長度為N的數組為O(N)。
3. 穩定性
- 定義:一個數組在排完序后,相等的值的相對次序不發生變化。
- 穩定性的作用:對引用類型的數據進行排序
4. 測試用例
隨機生成數組,用自帶的sort函數進行結果驗證。引用類型數據還要寫比對函數。
5. 遞歸和迭代
- 所有遞歸都可以改為迭代。遞歸是系統自己壓棧,迭代就是自己壓棧。
- 遞歸是自己調用自己,迭代是用while循環。
- 生產環境里不要用遞歸。
基于比較的的排序
時間復雜度不可能低于O(N*logN)
一、冒泡排序
- 時間復雜度:O(N^2)
- 額外空間復雜度:O(1)
- 是否可實現穩定性:是
1. 原理
從第一個數開始,比較相鄰的兩個數,大的數往下沉,每次排好未排序序列中的最大數。
2. 交換兩個數據的方法
(1)用temp變量保存。
var temp=a;
a=b;
b=temp;
(2)異或運算(XOR)。
異或滿足交換律和結合律,就是無進位相加,1+0=1,0+1=1,1+1=0。
缺點:float類型且引用地址相同不能用該方法。
a=a^b;
b=a^b;
a=a^b;
3. 時間復雜度
N+N-1+....+1=N(N+1)/2;只要高階項,不要低階項,也不要高階項系數,因此為O(N2)。
4. 空間復雜度
冒泡排序只需要幾個有限的變量空間,所以是O(1)。
5. 穩定性
相等的不交換,大于時交換,就可以保證穩定性。
二、插入排序
- 時間復雜度O(N^2)
- 額外空間復雜度O(1)
- 是否可實現穩定性:是
1. 原理(類似撲克牌)
插入排序就是,在有序區中,從后往前,遇到大于自身的數就進行交互換,看什么時候停下來。直到最后一個數加入有序區。
2.空間復雜度
最差情況下1+...+N-1=(N-1)N/2,因此為O(N2)。
3. 空間復雜度
只需要幾個有限的變量空間,所以是O(1)。
4. 穩定性
相等的不交換,只有大于時交換,就可以保證穩定性。
三、選擇排序
- 時間復雜度O(N^2)
- 額外空間復雜度O(1)
- 是否可實現穩定性:否
1. 原理
每次從無序區中選擇最小的數加入到有序區的末尾。與相應位置的數據進行交換
2.空間復雜度
每次遍歷找最小值:N+N-1+....+1=N(N+1)/2,因此為O(N2)。
3. 空間復雜度
只需要幾個有限的變量空間,所以是O(1)。
四、快速排序
- 時間復雜度O(N*logN)
- 額外空間復雜度必須為O(logN)
- 是否可實現穩定性:額外空間復雜度為O(logN)的情況下不可以,增加額外空間可以,但快排要求的就是空間復雜度為O(logN)。
logN默認以2為底,代表長度為N的數組可以二分多少次。
1、求M([3,1,5,4,2])中不在N([9,2,7])中的數的集合,即求M-N的差集
(1)暴力搜索
將M中每個數依次與N中的數進行遍歷對比,不在N中就打印,在N中就直接跳過。
時間復雜度O(MN)。
(2)先排序再二分
- N先排序,M中每個數通過二分搜索的方式確定有沒有。
- 排序時間復雜度為O(A),二分搜索時間復雜度為O(MlogN)。該方法的總時間復雜度為O(A)+O(MlogN),化簡后為其中的大者。
- L+(R-L)/2 = L+(R-L)>>1,位運算比除法快
2、隨機快排原理
- 經典快排:將小于等于劃分為一個區域。
- 改進快排:將小于和等于分為兩個區域。隨機找一個數作為基準,小于這個數的放左邊,等于的放中間,大于的放右邊,然后再遞歸將左右邊進行排序
3、代碼實現
(1)如果為null或者length<2,直接返回
(2)隨機選擇一個數作為基準數,交換至數組末尾
(3)partition返回等于基準數的左右邊界下標,再遞歸的對小于區和大于區進行快排。
4、時間復雜度
(1)選一個隨機的劃分數,為O(1)。
(2)partition過程,將數組劃分為三個區間,就是遍歷一次數組的過程為O(N)。
(3)左側和右側分別進行了遞歸。
- 最差情況下,每次O(N)只能搞定一個數(1,2,3,4,5,6選擇第一個數為劃分數),最終復雜度為O(N2)。因此通過隨機選擇劃分值可以在概率上盡量避免出現最差的情況。
- 最好情況下,左右差不多都是N/2,2T(N/2),用master公式計算,a=b=2,d=1,因此T(N)=N*logN。master公式只能用于遞歸規模一樣的情況。
- 概率累加得到的期望值也為N*logN。
5、空間復雜度
因為左右需要遞歸排序,過程中需要不斷記錄劃分值的位置,因此空間復雜度為斷點樹的高度,即log(2)(N)
五、歸并排序
- 時間復雜度O(N*logN)
- 額外空間復雜度O(N)
- 實現可以做到穩定性
1、原理
- 遞歸法:將數組二分直至不能劃分,即每個組都只有一個數值,然后不斷合并排序。
- 迭代法:room=1、2、4、8....不足就保持不變,不斷排序合并
2、小和問題
- 問題:將每個數的左邊所有比它小的數進行累加,得到的和為小和。
- 思路:對每個數而言,只需要知道我的右邊有多少個數比我大,我就累加幾次。通過歸并排序,在merge的的過程中,會依次遇到所有右邊比我 大的數。
3、求逆序對
- 問題:存在多少對數對,數對中前面的數比后面的數大。
- 思路:和小和問題同理,即對每個數而言,只需要知道我的右邊有多少個數比我小,在我這個位置就產生了多少對逆序對。
六、堆排序
- 時間復雜度O(N*logN)
- 額外空間復雜度O(1)
- 實現不能做到穩定性
關鍵步驟:heapInsert, heapify,堆的擴大和縮小操作
1、堆的概念
- 完全二叉樹:葉節點只能出現在最下層和次下層,并且最下面一層的結點都集中在該層最左邊的若干位置的二叉樹。
- 平衡二叉樹(AVL樹):它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,并且左右兩個子樹都是一棵平衡二叉樹,同時,平衡二叉樹必定是二叉搜索樹,反之則不一定。
- 堆(算法上的堆,不是系統中的堆)就是一個完全二叉樹,可以把一個數組在邏輯上對應成一顆完全二叉樹,左孩子2i+1,右孩子2i+2,父(i-1)/2。
- 大根堆:一定是一個完全二叉樹結構,任何一個根節點都是其所在數樹(子樹)中的最大值。
2、原理
(1)將數組變換為大根堆。每個數和父節點的值進行比較,如果大于父節點,就交換。時間復雜度=log1+log2+log3+...+logN=O(N)。
(2)此時根節點的值最大,然后交換根節點和最后一個節點,保持最后一個節點不動,size--,超過size就代表越界。然后再構建大根堆,以此往復,每次找打最大值,直至所有數據排完序。
最重要的heapinsert(初始化構建大根堆),heapify(下沉函數),size。優先級隊列的題目就是堆,建立堆的過程和任何一個堆沉下去的過程重要。
4、時間復雜度
(1)建立大根堆的過程為O(N)。
(2)heapify的過程就是樹的高度,為O(logN)。
(3)N個數的時間復雜度就是O(NlogN),但常數項很大。
5、堆排和快排對比
- 空間復雜度:堆排的空間復雜度可以做到O(1)是因為它的父子節點是通過公式的方式找到的,而快排需要記錄斷點的位置。
- 時間復雜度:堆排的常數項很大,不存在最好和最壞情況;快排的常數項小。
七、希爾排序
插入排序,就是步長為1的希爾排序。
八、系統中的排序原理
- 數據量<60,插入排序,復雜度高,常數項少;
- 數據量大的情況下,快速排序。
不基于比較的的排序
不基于比較的排序是有瓶頸的,對數據的位數和范圍有限制。
桶排序
桶排序只是一個概念,基數排序和計數排序是桶排序的具體實現。
一、計數排序
先準備好相應數量的桶,然后遍歷數據,將其放入對應的桶中,最后將桶中的數依次倒出來。
二、基數排序
- 取位數最多的記錄,其他不足該位數的補0。
- 按照個位數字依次放入桶中。
- 從0號桶開始,依次倒出來。先入桶的先倒(隊列結構)。
- 按照十位數進桶,再倒出來,如此反復,直到最高位,結束。