《演算法導論》筆記(15) 最小生成樹 部分習題
習題23.1-11 給定圖G和一棵最小生成樹T,假設減少了位於T之外的某條邊的權重。因為T內的邊,是連線所有結點的權重最小的,那麼首先將T外的減少權重的邊(u, v)加入T,然後在u, v中尋找所有的路徑,去掉路徑中權重最大的邊。
習題23.2-3 使用斐波那契堆實現的Prim演算法與使用二叉堆比較。因為斐波那契堆實現優先佇列,使Prim演算法的執行時間為O(E+VlgV),而二叉堆實現優先佇列的Prim演算法執行時間為O(VlgV+ElgV),前者更小。同時,稠密圖使用斐波那契堆實現Prim演算法更有優勢。因為E越大,需要更新堆中元素次數越多。
習題23.2-4 邊權重為1~|V|整數的圖的Kruskal演算法。邊排序用計數排序,時間為O(E),有O(V)次make_set與O(E)次find_set和union,總時間為O(E+α(V)*(E+V))。如果邊權重為1~W,則取決於W與|V|中最大值。
習題23.2-5 邊權重為1~|V|整數的圖的Prim演算法。若最小優先佇列用斐波那契堆,總執行時間為O(E+VlgV)。前一項代表執行更新堆中元素值的時間,後一項代表出堆時間。但若W<lgV,最小優先佇列用一個1~W的計數桶來實現。每次出列操作的時間為O(W),則總時間為O(E+V*W)。
習題23.2-6 圖中所有邊權重均勻分佈在[0,1)內的Prim演算法與Kruskal演算法比較。邊的排序用桶排序,時間為O(E),對於kruskal演算法,有O(E)次find_set和union,O(V)次make_set。執行時間為O(E+α(V)*(E+V)),而用了斐波那契堆作最小優先佇列的Prim演算法,總時間為O(E+VlgV)。可見,Kruskal演算法更快。
習題23.2-7 圖的最小生成樹已經被計算出來,加入新結點與邊,更新最小生成樹的時間。可以將與新結點相連的結點從最小生成樹中刪除,然後只考慮這些結點的邊,用Prim或者Kruskal演算法重新生成最小生成樹。
思考題23-1 次優最小生成樹,相對於最小生成樹只有一條邊不同,即去掉最小生成樹上一條邊,加入另一條原本沒有的邊。假設去掉的邊為E(u,v),加入的邊為E'(j,k),E'-E是增加的權重,那麼可能有多種u、v、j、k的選擇,得到相同的E'-E。
將全部的點分為兩個連通集,很顯然,連線兩個連通集的所有邊中,最小權重邊是最小生成樹的邊,而次大權重的邊是次優最小生成樹的邊。假設已有一最小生成樹,然後檢查每條邊,將此邊刪除,分最小生成樹為兩個連通集,並找到次大的邊使連通集合並,得到新的生成樹的權重。同樣的方法操作所有邊,選擇最小權重的生成樹即為次優生成樹。顯然,次優生成樹可能有不止一種。取決於刪除的邊與加入的邊權重之差。
計算max[u, v],可以按照BFS搜尋的方式,搜尋出全部的路徑,然後統計出路徑中最大的邊。時間O(V^2)。
計算次優最小生成樹的演算法,可以先計算最小生成樹,然後在原圖中測試每一條不在最小生成樹的邊(u,v),尋找最小生成樹中的max(u,v),令d= w(u, v) - max(u, v),得到所有的d然後尋找最小值。對應的(u, v)替換最小生成樹中的max(u, v),為次優最小生成樹。
思考題23-2 稀疏圖的最小生成樹。每一次收縮的過程,實質就是選擇連線兩個連通集的最小邊過程。整個過程相當於分治法,先分成兩個一組的連通集,然後這些小連通集兩兩合併,每次合併,都選擇最小權重的邊,直到所有連通集合併為一個。足以保證最終得到的就是最小生成樹。
MST-Reduce實現的關鍵是set的結構。對所有邊的遍歷,都要查詢兩個端點所在的set,但每趟reduce過程合併一次set,後面全部是查詢操作,那麼只需要對每個元素標記一次根元素就可以了。執行時間是先生成set為O(E),遍歷邊是O(E),所以總執行時間是O(E)。若執行k趟reduce過程,每次迭代後,結點數量變為原來的一半V/2,邊數量變為E-V/2,則k趟執行總時間是O(k*E- (V/2+ V/4...+V/2^k) )。,後項的極限為2V,故總時間為O(k*E)。
因為最後執行Prim時的複雜度為O( E‘ + V'lgV' ),其中E'= E-V/2 -V/4 -...-V/(2^k)為最後的邊數,V'=V/(2^k)為最後的結點數,則總複雜度為O( k*E +E’ + V'lgV' )。問題等價於取k*E+ V/(2^k)*(lgV- k)的最小值。K應當取小於lgV但是大於1的整數。求導並化簡,忽略增長緩慢的項,E-VlgV*k/2^k=0,得k/2^k = E/VlgV。有2^(k-lgk)=VlgV/E,k=O(lg(VlgV/E))。總執行時間為O(kE+V/2^k*lgV)= O(kE)= O(E*lg(VlgV/E)),又因為E、V相差不超過平方量級,可等視之,總執行時間為O(E*lglgV)。
由2^(k-lgk)=VlgV/E,E=VlgV/(k-lgk),故執行帶預處理的Prim演算法的條件是K有正整數解,E<VlgV.
思考題23-3 瓶頸生成樹。首先,不可能有瓶頸生成樹的T值小於最小生成樹。假設瓶頸生成樹去掉最小生成樹的一條邊而加入另一條邊,則必然超過了最小生成樹的最大邊權重。否則兩連通分量之間引入了非最小邊。
線性時間演算法判斷瓶頸生成樹的T值。判斷T值是否大於b,可DFS從任意結點開始遍歷圖,途中若有超過b的邊則刪除,終止時若不能遍歷所有結點,圖不連通,說明瓶頸生成樹的T值不超過b。
將全部邊按照權重大小分為2個部分,權重小的子集按照DFS遍歷。若不連通,證明瓶頸生成樹的T值小於子集最大權重邊。則將此子集再分為兩半並以相同方法DFS遍歷。若連通,則收縮至一個點,與另外一半子集合並重新查詢。重複多次直到全部收縮為1個點並找到T值。時間為O(E)+ O(E/2)+ O(E/4)+......,極限為O(2E)= O(E)
思考題23-4 第三種最小生成樹演算法。
Maybe_MST_A(),正確。任意兩個連通集,先刪掉的是權重大的邊,最後保留的是權重最小的邊。
Maybe_MST_B(),不對。加入的邊沒有環路,只能保證生成樹,但是不能保證邊都是最小的權重,因為順序是任意的。
Maybe_MST_C(),正確。加入的邊沒有環路,則任意點只有一條邊與外界相連,每次操作最多構成一個新的連通子集。每次形成環路後刪除最大權重的邊,保證了任意兩個連通子集之間的路徑都是最小的。故可以得到最小生成樹。