數據結構與算法分析

數據結構與算法分析

C 語言描述

引論

  • 從N個數中肯定第k個最大值,稱爲選擇問題(selection problem).
  • 不是全部的數學遞歸函數都能有效地(或正確地)由C的遞歸模擬來實現. 遞歸將反覆進行直到基準情形出現.
  • 遞歸的四條基本法則:
    • 基準情形: 不需遞歸也能獲得的解, 即終止條件.
    • 不斷推動: 每次遞歸調用都要使解朝向基準情形推動.
    • 設計法則: 假設全部的遞歸調用都能運行.
    • 合法效益法則(compound interest rule): 在不一樣遞歸調用中不要作重複性的工做.

算法分析

  • 算法(algorithm)是爲求解一個問題須要遵循的, 被清楚地指定的單一指令的集合.
  • 分治策略(divide and conquer):
    • 分: 把問題分紅兩個大體相等的子問題, 而後遞歸地求解.
    • 治: 將兩個子問題的求解合併到一塊兒並求解, 最後獲得整個問題的解.

表, 棧和隊列

  • 抽象數據類型(abstract data type, ADT)是一些操做的集合.
  • 後綴表達式: 計算後綴表達式花費的時間是O(n), 每一步處理是棧操做的常數時間.
  • 利用棧將一個標準形式的表達式轉換爲後綴式:
    • 當讀到一個操做數的時候,當即把它放到輸出中;
    • 遇到操做符, 用棧緩存;
    • 遇到左括號也入棧;
    • 遇到右括號,就一直彈出至一個左括號;
    • 遇到符號, 彈出至低優先級符號爲止.

  • 二叉查找樹(binary search tree).
  • 先序遍歷(preoder traversal): 根左右.
  • 後序遍歷(postorder traversal): 左右根.
  • 中序遍歷(inorder traversal): 左根右.
  • 二叉樹:每一個節點不能多於兩個兒子.
    • 二叉樹的平均深度要比N小得多 --- 爲(O(根號(N))).
    • 二叉查找樹的平均深度爲O(logN).
  • 表達式樹: 表達式樹的葉子是操做數(operand), 其餘節點是操做符(operator).
    • 中序就是中綴表達式;
    • 後序就是後序表達式;
  • AVL樹是帶有平衡條件的二叉查找樹, 經過旋轉(rotation)來保證樹的平衡.
    • 插入發生在外邊(左-左或右-右) --- 經過一次單旋來調整.
    • 插入發生在內部(左-右或右-左) --- 經過雙旋來處理.
  • 伸展樹(splay tree): 保證從空樹開始任意連續M次對樹的操做最多花費O(MlogN)時間.
    • 一顆伸展樹每次操做的攤還代價是O(logN).
    • 當一個節點被訪問後, 要通過一系列AVL樹的旋轉被放到根上. (適用於一個節點被訪問,不久後會再次被訪問).

B- 樹

  • B- 樹是經常使用的查找樹, 但不是二叉樹.
  • 階爲M的B-樹具備下列特性:
    • 樹的根或者是一片樹葉,或者兒子數在2和M之間.
    • 除根外, 全部非樹葉節點的兒子數在[M/2]和M之間.
    • 全部的葉子都在相同的深度上.
  • 4階的B- 樹被稱爲2-3-4樹, 3階的B- 樹被稱爲2-3樹.
  • B- 樹實際用於數據庫系統, 在哪裏樹被存儲在物理的磁盤上而不是主存中.

散列

  • 散列表的實現經常叫作散列(hashing).
  • 散列是一種用於常數平均時間執行插入, 刪除和查找的技術.
  • 散列最主要的是解決衝突(collision) --- 兩個關鍵字散列到同一個值的時候.
  • 散列函數: 一般保證散列表的大小爲一個素數.
    • 取餘法: Key mod TableSize;
    • 儘可能讓鍵值分佈均勻.
  • 解決散列衝突的方法:
    • 分離連接法;
      • 將散列到同一值的全部元素保留到一個表中.
    • 開放定址法;
      • 若是發生衝突, hash嘗試選擇另外的單元, 直到找出空的單元爲止.
      • 線性探測法;
      • 平方探測法;
      • 雙散列;
      • 再hash; --- 表滿一半就再散列; 途中策略(middle-of-the-road) --- 當表到達某一個裝載因子的時候再進行再散列.
  • 處理數據大以致於裝不進主存的狀況:
    • 可擴散列(extendible hashing) --- 容許用兩次磁盤訪問執行一次Find操做.
    • 用D表明根所使用的比特數, 稱其爲目錄(directory), 目錄中的項數爲2^D, dL爲樹葉L全部元素共有的最高爲的位數, dL <= D.
    • 若是不夠存放插入的新關鍵字, 那麼樹葉會被分紅兩片新的樹葉, 目錄大小(比特的位數)將加1.
    • 這種簡單的方法提供了大型數據庫Insert操做和Find操做的快速存取時間.
  • 假設位模式(bit pattern)是均勻分佈的
  • 目錄的大小(2D)爲O(N(1+1/M)/M), 若是M很小. 那麼目錄可能過度大.
    • 樹葉包含指向記錄的指針而不是實際的記錄, 這樣能夠增長M的值.

優先隊列(堆)

  • 優先隊列(priority queue) 也稱之爲堆. --- 使用二叉查找樹實現優先級隊列.
  • 二叉堆(binary heap)具備兩個性質:
    • 結構性; --- 堆是一個徹底二叉樹, 樹高O(logN). --- 徹底二叉樹能夠用一個數組表示而不須要指針.
      • i位置的元素, 其左兒子在位置2i上, 右兒子在左兒子後的單元(2i+1)中, 父親在[i/2]上.
    • 堆序性;
      • 使操做快速執行的性質是堆序性(heap order), 最小堆須要最小元素在根節點上.
    • 對堆的一次操做可能會破壞掉這兩個性質.
  • 二叉堆插入節點時, 須要執行上慮策略(percolate up), 直到新元素找出正確的位置.
    • 將新元素插入到滿二叉樹的末尾, 而後檢查是否父節點比其大, 若是大的話就與父節點交換位置.
  • 刪除須要一種下濾的策略(percolate down):
    • 刪除根節點後, 須要將滿二叉樹的最後一個元素正確的放到堆中, 先放到根節點, 再繼續和左右孩子進行比較, 選擇小的進行交換.
  • 優先隊列的應用:
    • 選擇問題;
    • 事件模擬;
  • d-堆是二叉堆的簡單推廣, 全部的節點都有d個兒子;
    • 當優先隊列太大不能徹底裝入主存的時候, d-堆是一個不錯的選擇.
  • 左式堆:
    • 左式堆(leftist heap)也具備結構特性和有序性, 左式堆也是一個二叉樹.
    • 左式堆不是理想平衡的, 傾向於很是不平衡.
    • 左式堆的左兒子的領路徑長至少與右兒子的領路徑長同樣大.
    • 左式堆傾向於加深左路徑, 因此有路徑應該短.
    • 左式堆最主要的應用是在堆的合併上.
  • 斜堆:
    • 斜堆(skew heap)是左式堆的自調節形式, 實現起來極其簡單.
    • 斜堆與左式堆的關係相似於伸展樹與AVL樹之間的關係.
    • 斜堆具備堆序的二叉樹, 可是不對樹的結構進行限制.
  • 二項隊列結構:
    • 一個二項隊列結構, 不是一棵堆序的樹, 而是堆序樹的集合, 稱爲森林(forest).
    • 二項樹的每個節點將包含數據, 第一個兒子以及右兄弟, 二項樹中的各個兒子以遞減次序排序.

排序

  • 插入排序(insertion sort);
  • 希爾排序(shell sort)也稱爲縮小增量排序(diminishing increment sort).
  • 堆排序(heap sort)在實踐中慢於Sedgewick增量序列的希爾排序.
  • 歸併排序(mergesort): 經過遞歸實現.典型的分治策略(divide-and-conquer).
    • 每一個遞歸調用, 局部都會聲明一個臨時數組.
  • 快速排序(quicksort)是實踐中最快的已知排序算法, 快排也是一種分治的遞歸算法.
    • 基本步驟:
      • 若是中元素個數是0或1, 則返回.
      • 取S中任一元素v, 稱之爲軸點(pivot);
      • 將S-v(S中剩餘元素)分紅兩個不相交的集合, S1和S2.
      • 返回quicksort(S1), v, 和quicksort(S2).
    • 隨機選取樞紐元, 三數中值分割法.
  • 小數組快排沒有插入排序快(N <= 20);
  • 桶式排序(bucket sort).
  • 外部排序(external sorting): 用來處理輸入很大的數據.
    • 基本的外部排序是基於歸併排序中的Merge過程.
    • 多路合併.
    • 多相合並.
    • 替換選擇.

不相交集ADT

  • 等價關係:
    • 自反性;
    • 對稱性;
    • 傳遞性.
  • 保持不相交集合是很是簡答的數據結構.
  • 路勁壓縮是自調整(self-adjustment)的最先形式之一, (伸展樹和斜堆).

圖論算法

  • 深度優先搜索(depth-first search)是一個重要的技巧.
  • 一個郵箱無圈圖有時被稱爲DAG.
  • 若是無向圖中從每一個頂點到其餘頂點都存在至少一條路徑, 則稱該無向圖是連通的(connected).
  • 若是有向圖具備這樣的性質稱爲強連通(strongly connected).
    • 若是一個有向圖不是強連通, 可是是基礎圖(underlying graph) --- 去掉方向所造成的圖是連通的, 則稱該有向圖爲弱連通(weakly connected).
  • 徹底圖(complete graph)是指每一對頂點之間都存在一條邊的圖.
  • 圖能夠經過鄰接矩陣和鄰接表進行表示.

拓撲排序

  • 拓撲排序是對有向無圈圖的頂點的一種排序, 若是存在一條vi到vj的路徑, 那麼在排序中vj出如今vi的後面.
  • 拓撲排序並非惟一的, 任何合理的排序都是能夠的.
  • 找出任意一個沒有入邊的頂點, 顯示出該頂點, 而後將它和它的邊一塊兒從圖中刪除.

最短路徑算法

  • 單源最短路勁問題: 找到給定頂點s到G中每一個其餘頂點的最短路徑.
  • 廣度優先搜索(breadth-first search): 按層處理頂點, 距開始點最近的那些頂點首先被訪問, 最遠的點最後被訪問, 有點像樹的層次遍歷.
  • dijkstra算法:
    • 每一個頂點都要保留一個臨時距離dv = 源點s到v的最短路徑長.
    • 採用的是一種貪婪的策略(greedy algorithm). 貪婪算法通常分階段求解一個問題, 在每一個階段它把當前出現的看成最優解去處理.
      • 貪婪算法不是總能成功.
    • 已知的鄰接點的臨時距離須要不斷調整直到最小的值.
    • 能夠寫一個遞歸程序跟蹤p數組留下的蹤影.
    • 對於稀疏圖, 使用優先級隊列對dijkstra最短路徑算法進行優化.
  • 具備負邊值的圖, Dijstra算法是行不通的, 也不能向每條邊增長一個常量值delta進行去負邊.
  • 無圈圖, 能夠經過拓撲排序來改進Dijkstra最短路徑算法, 即選擇和更新在拓撲排序執行的時候進行.
    • 無圈圖能夠模擬某種滑雪問題;
    • 無圈圖的一個更重要的用途是關鍵路徑分析法(critical path analysis).
  • 全部點對最短路徑問題, 多源多匯最短路徑.

網絡流問題

  • 解決最大流問題, 最簡單的思路是分階段解決, 構建一個殘餘流的圖(residual graph), 對應殘餘邊(residual edge).
    • 從殘餘圖Gr中尋找一條s到t的一條路徑, 稱這條路徑爲增廣通路(augmenting path).
    • 一旦注滿一條邊(使飽和), 則這條邊就要從殘餘圖中去除.
    • 最小費用流問題, 每條邊不只有容量, 並且每一個單位流還存在價格, 在最大流中找一個最小价格的流來.

最小生成樹

  • 最小生成樹(minimum spanning tree), 無向圖中找最小生成樹比較容易, 在有向圖中比較困難.
    • 無向圖中的最小生成樹就是由該圖的那些鏈接G的全部頂點的邊構成的圖, 其總價值最低.
    • 包含途中全部頂點的最小的樹.
  • Prim算法: 計算最小生成樹時, 使其連續地一步步生成, 在每一步都要把一個節點看成根往上加邊.
    • 每一步添加一條邊和一個頂點到最小生成樹上.
    • 與dijkstra最短路徑算法相似.
  • Kruskal算法; 連續地按照最小的權值選擇邊(貪心的策略).

深度優先搜索

  • 深度優先搜索(depth-first search)是對先序遍歷(preorder traversal)的推廣.算法

  • 無向圖是連通的.shell

  • 若是無向圖任一頂點刪除後, 圖還是連通的, 那麼這個無向連通圖稱爲雙向連通的.數據庫

  • 若是一個圖不是連通的, 刪除一點後圖再也不連通, 那麼這個頂點就叫作割點.數組

  • 歐拉回路:緩存

    • 終點必須終止在起點上的歐拉回路只有當圖是連通的而且每一個頂點的度(邊的條數)是偶數才又節能存在有效解.
    • 全部頂點的度(邊的條數)均爲偶數的任何連通圖必然有歐拉回路.
    • 哈密爾頓圈問題(Hamiltonian cvcle problem) 是一個NP難問題.
      • 哈密爾頓圈問題是要找一個圈, 該圈包含全部的頂點.
  • NP徹底性:網絡

    • 存在大量重要的問題, 他們在複雜性上大致是等價的, 這一類問題被叫作NP徹底(NP-complete)問題.
    • 這些NP徹底問題精確度的複雜性仍然須要肯定而且在計算機理論科學方面仍然是最重要的開放性問題.
    • 要麼NP徹底問題都有多項式時間解法, 要麼都沒有多項式時間解法.
    • NP(Nondeterministic polynomial-time)非肯定型多項式時間.
    • NP中的任意問題都能多項式歸約爲NP徹底問題.
    • NP徹底問題是NP問題中最難的問題.
  • NP徹底問題的例子:數據結構

    • 哈密爾頓圈問題是NP徹底問題.
    • 巡迴售貨員是NP徹底問題. 徹底圖小於K值的簡單圈.
    • 最長路徑問題.
    • 裝箱問題(bin packing).
    • 揹包問題(knapsack).
    • 圖的着色(graph coloring)問題.
    • 團的問題(clique).

算法設計技巧

  • 求解問題的五種一般類型的算法.

貪婪算法

  • 貪心算法分階段工做, 在每個階段, 認爲鎖決定是好的, 不會考慮未來的後果.
    • 通常獲得的解都是局部最優解, 即次優解.
  • 任務調度問題;
  • Huffman編碼 --- 文件壓縮問題.
    • 表明字母的二進制代碼用二叉樹來表示, 每一個字符經過從根節點開始用0指示左分支用1指示, 右分支而以記錄路徑的方法表示出來. --- 這種樹叫作trie數.
    • 總價值最小的滿二叉樹.
    • 哈夫曼算法:
      • 算法對一個由樹組成的森林進行;
      • 一棵樹的權等於它的樹葉的頻率和.
      • 任意選取最小權的兩顆樹T1和T2, 並任意造成新樹, 進行C-1次;
      • 存在C棵單節點樹 --- 每一個字符一顆樹;
      • 算法結束時獲得一顆樹, 這棵樹就是哈夫曼編碼樹.
      • 最優前綴碼.

近似裝箱問題

  • 聯機算法:
    • 下項適合算法(next fit): 檢查剛剛裝進物品的箱子是否還能裝.
    • 首次適合算法(first fit): 依次掃描箱子, 把新的物品放入足夠空間的箱子.
    • 最佳適合算法(best fit): 把一個物品放到全部箱子中可以容納它的最滿箱子中.
  • 脫機算法:
    • 首次適合遞減算法(first fit decreasing);
    • 最佳適合遞減算法(best fit decreasing).

分治算法

  • 分治算法(divide and conquer)由兩部分組成:
    • 分(divide): 遞歸解決較小的問題.
    • 治(conquer): 用子問題的解構造原問題的解.

動態規劃

  • 動態規劃(dynamic programming)解決數學遞推公式的一種技巧, 當前狀態與以前的狀態相關.
  • 最優二叉查找樹.

隨機化算法

  • 一個隨機化算法的最壞運行時間幾乎老是和非隨機化算法的最壞運行時間相同.
  • x(i+1) = Ax(i) mod M x(0)叫作隨機種子.
  • 跳躍表(skip list): 是在單鏈表上的擴展, 每一個節點具備k個指針(每一個節點的階是隨機肯定的), 能夠跳躍式地指向其餘節點.
  • 跳躍表相似於散列表, 它們都須要估計表中的元素個數(從而階的個數能夠肯定);
    • 跳躍表如許多平衡查找樹實現方法同樣有效.

回溯算法

  • 回溯算法(backtracking)至關於窮舉搜索的巧妙實現.
  • 收費公路重建問題.
博弈
  • 極小極大策略, 利用置換表(散列實現)節省大量的計算.ide

  • a-b裁剪(a-b pruning): 不須要進行求值的叫作a裁剪, 不會影響到min層的結果, 叫b裁剪.函數

  • 攤還界比最壞情形界要弱, 但比等值的平均情形要強, 攤還要考慮整個操做序列而不是僅僅一次操做.post

  • 紅黑樹:

    • 每個節點或者着色爲紅色, 或者着色爲黑色.
    • 根是黑色的.
    • 若是一個節點是紅色的, 那其子節點都必須是黑色的.
    • 從一個節點到一個NULL指針的每條路徑必須包含相同的黑色節點數.
  • 1-2-3肯定性跳躍表.

  • BB- 樹是帶有一個附加條件的紅黑樹: 一個節點最多能夠有一個紅兒子.

  • AA結構要求從顏色轉換成層次.

    • 水平鏈接是同一層次上的兒子之間的鏈接.
  • treap樹是一種二叉查找樹, 像跳躍表同樣使用隨機數而且對任意輸入都能給出O(logN)的指望時間性能.

  • k-d樹.

  • 匹配堆. 最實用的斐波那契堆的變種, 具備兄弟指針, 前向指針(不表明父節點).

相關文章
相關標籤/搜索