標籤:演算法之道 排序 搜尋 最短路徑 散列
《演算法之道》精華 經典演算法部分
- 本書作者縐恒明,作者另有一本書《資料結構之弦》,以及《作業系統之哲學原理》都是很好的書
- 這本書可以算得上是深入淺出,文筆很好,作者添加了很多自己的思考
- 本文包括經典演算法部分
第十章 排序與次序
- 插入排序
- 從無序部分抽取一張插入有序部分
- 為原地排序,無需佔用臨時儲存空間
- 最優情況下為O(n),平均O(n^2)
- 折半插入排序
- 歸併排序
- 分治,從中間分解,分別排序後進行仔細的合并
- 異地排序,需要佔用額外空間
- n>=30時效能比插入排序更好。複雜度固定為O(nlog(n))
- 快排
- 分治,複雜的部分在於分解,而歸併複雜在於合并
- 原地排序
- 最壞情況為O(n^2),但只要不是每次都是最壞,複雜度就不是n^2,具有韌性
- 任何基於比較的排序,決策樹高度至少為nlog(n)
- 計數排序
- 基數排序
- 從最低位到最高位排序,每一位排序都採用穩定排序,如計數排序
- 一位排序應該選擇log(n)個位元,使整體成本最低
- 桶排序
- 把n元素按值分到n個桶裡,每個桶內部進行插入排序,將各桶首位相連
- 元素應該是均勻分布
- 快速次序選擇:求第K大的數
- 使用快排的partition
- 最差O(n^2),平均O(n)
- 線性最差快速次序選擇
- 將元素每5個一組,分別取中值。在n/5個中值裡面找到中值,作為partition的pivot
- 為什麼*不每3個一組?
- 保證pivot左邊右邊至少3n/10個元素
- 最差O(n)
第十一章 搜尋與散列
- 順序搜尋
- 在序列裡面如果搜尋頻率從頭到尾指數遞減,則為O(1)
- 折半搜尋
- 常數搜尋:散列搜尋
- 直接散列:非常簡單,不會發生碰撞,空間浪費大
- 除法(模除法)散列
- 元素對散列表大小m模數得到
- m必須為素數,否則造成不均勻散射。比如m包含因子d,而大部分元素對d餘數相等
- m不能靠近2的冪。如m為2的冪,散列結果將不依賴元素的所有位。靠近也不行,為什嗎?
- 乘法散列
- h(k) = (A * k ) % 2^r >> (w - r),w為電腦字寬,A為2^(w-1)與2^w之間的一個奇數
- 乘方取中法:乘方n次(常取n=2),取中間r位
- 開放定址散列:散列碰撞時縱深擴充,添加一個鏈表
- 封閉定址散列:散列碰撞時為元素找到另一個位置
- 找另一個位置的操作稱為探尋
- 線性探尋
h(k,i) = (h‘(k) + i) % m,h‘(k)為家位
- 向單方向尋找未被佔用的位置
- 易出現頂級聚集
- 非線性探尋
- 平方探尋
h(k,i) = (h‘(k) + c1 * i + c2 * i^2) % m 易出現次級聚集
- 雙重散列探尋
- 使用兩個散列函數h1、h2來構造新散列函數
h(k,i) = (h1(k) + i * h2(k) ) mod m
- 偽隨機探尋
- 不成功搜尋的探尋次數期望為
1/(1-a)
- 成功搜尋探尋次數最多為
1 / a * ln( 1/(1-a))
- 封閉散列不能刪除元素,可以放標記解決。如果插入相比搜尋非常稀疏,則可以通過重新散列解決空位問題
- 隨機化散列
- 找到一組散列函數,每次隨機播放一個不同的散列函數
- 用於避免單個散列函數極端情況下聚集效應嚴重
- 全域散列
- 一組H個散列函數,將任意兩個不同的元素映射到同一位置的函數個數為H/m
- 完美散列
- n個元素,構造m=O(n)大小的散列表,使搜尋最壞達到O(1)
- 採用雙層散列,第一層大小n,第二層每個表的大小為落到第一層位置i上的元素個數的平方
- 空間消耗為O(n)
第十二章 最短路徑
- 如果圖中有負環,則不存在最短路徑
- 單源多點最短路徑
- Dijkstra演算法
- 貪婪演算法,要求不存在負路徑
- 最優子結構:最短路徑裡的每一段都是兩點之間的最短路徑
- 貪婪選擇屬性:路徑向外延伸的下一個節點就是離源點最近的節點
- 每次選取離源點最近的節點,更新所有與此節點相鄰節點的距離
- 時間複雜度為O(V^2),採用堆實現,可以達到O(E log(V))。與Prim演算法相同
- Bellman-Ford演算法
- 可以應對負權重
- 進行V-1輪降距,每次更新圖中所有邊
- 複雜度為O(VE)
- BFS
- 多源多點最短路徑
- Floyd-Warshall演算法
- 動態規划算法
- 子問題為從i到j,中間結點只屬於集合1...k的最短路徑長度
- c_ijk = min{c_ij(k-1), c_ik(k-1) + ckj(k-1)}|k
- 複雜度O(n^3)
- Jonhson演算法
- 等效變換為無負權重的圖,使用Dijkstra演算法
- 添加一個節點s,到所有點路徑長度為0,運行Bellman-Ford演算法,對節點賦值
- 對每個節點運行Dijkstra演算法
- 複雜度主要是Dijkstra演算法運算,為O(VE + V^2 log(V))
- 若Bellman-Ford演算法報告有負環存在,不能使用此方法
轉載請註明Focustc,部落格地址為http://blog.csdn.net/caozhk,原文連結為點擊開啟