2)多說一句
這一部分,資料結構已經不再重要了,開始進入到了演算法設計的領域。在《資料結構、演算法與應用》一書中,描述了一部分重要的演算法設計方法,包括貪婪演算法、分治演算法、動態規劃、回溯和分支定界。並且對於演算法的討論不如《演算法導論》中細緻周密。資料結構專欄暫時只關注貪婪、分治和動規三種演算法設計方法。
貪婪演算法的演算法設計思想,每一步,我們都在一定的標準下,作出乙個最優決策。在每一步做出的決策,在以後的步驟中都不可更改。做出決策所依據的標準稱為貪婪準則。
在貪婪演算法中,我們每一步都依據貪婪準則,作出乙個區域性最優解,所有區域性最優解結合起來便是貪婪演算法給出的解。因此貪婪演算法「目光短淺」,沒有全域性意識。在部分不需要結合全域性的問題中可以找到最優解,在其它題目中則不一定能找到最優解,作為啟發式演算法使用。
a. 問題描述
在有向無環圖(directed acycline graph, dag)應用領域中,aov網和aoe網是兩種不同形式的重要應用。
aov網:頂點活動(activity on vertex)網路。頂點代表任務,有向邊(i,
j)(i, j)
(i,j
)表示認為的先後關係,在任務j
jj開始前,任務i
ii必須完成。
下圖是乙個典型的aov網路,任務3開始之前,任務1必須完成;任務4開始之前,任務123必須完成。序列123456、132456、215346都為滿足要求的拓撲排序,可見同一aov圖的拓撲排序不唯一。
拓撲排序的應用領域非常廣泛,例如建築的搭建、汽車的組裝、知識學習的順序等等都包含拓撲的思想。
我們要解決的問題:給出乙個aov網,設計乙個演算法給出乙個拓撲序列。
b. 演算法思想選擇及求解
在本篇文章中我們當然是選擇貪婪演算法思想,我們首先要想出乙個貪婪準則(這裡可以暫停想一想哈哈)。
演算法過程:拓撲序列開始是乙個空序列,我們每次從滿足貪婪準則的未加入序列的頂點中選擇乙個頂點加入到拓撲序列中,直到找不到滿足貪婪準則的頂點,演算法結束。
貪婪準則:它沒有入邊或者存在入邊(v,
w)(v,w)
(v,w
),入邊可能存在多條,對於每條入邊,起始頂點v
vv已經存在於拓撲序列中。
貪婪準則(簡潔版):從剩餘的頂點中選擇乙個頂點w
ww,它沒有這樣的入邊(v,
w)(v,w)
(v,w
),其中頂點v
vv不在序列中。
簡潔版是書上的定義,很精練,但理解的時候需要繞一下彎。
明確貪婪準則和演算法過程後,便可試著寫出偽**。通過偽**在腦中跑跑程式,在紙上模擬一下過程。
/**
if(theorder的頂點數小於n)
演算法失敗
else
theorder是乙個拓撲序列
}*/
c. 驗證演算法的正確性
如果上述偽**演算法成功,則圖中必有乙個拓撲序列。驗證演算法的正確性,即驗證當演算法失敗時,圖中沒有拓撲序列。
實際上,當演算法失敗時,圖中必有環路。
證明:
d. 資料結構的選擇
這一步是進行**實現的準備工作。
我們根據偽**來判斷**需要資料結構和使用什麼樣的資料結構。
e. c++**實現
bool
topologicalorder
(int
* theorder)
for(
int i =
1; i <= n; i++
)// 計算每個頂點的入度
} stack<
int> s;
// 初始入度就為零的頂點,壓入棧中。
for(
int i =
1; i <= n; i++
)// 產生拓撲序列
int j =0;
while
(!s.
empty()
)}return
(j == n)
;}
f. 演算法複雜性分析
時間複雜性:
通過上一節對圖的討論我們得知,對圖進行遍歷,鄰接矩陣需要的時間複雜度為o(n
2)o(n^2)
o(n2
), 鄰接鍊錶的時間複雜度為o(n
+e)o(n+e)
o(n+e)
。在上述演算法中,第二個for迴圈和最後的兩個while巢狀迴圈,其實是對圖做了最多兩遍遍歷,鄰接矩陣和鄰接鍊錶的時間複雜度分別為o(n
2)o(n^2)
o(n2)和o(n
+e)o(n+e)
o(n+e)
。第一、三個for迴圈時間複雜度為o(n
)o(n)
o(n)
。所以總時間複雜度:鄰接矩陣o(n
2)o(n^2)
o(n2
);鄰接鍊錶o(n
+e)o(n+e)
o(n+e)
。空間複雜性:
由於陣列和棧的使用,空間複雜度:o(n)
一開始學演算法的時候,以為只有**實現是重要的。現在看來大錯特錯。寫出拓撲排序演算法的過程看似有些繁瑣,但是卻完成了從現實問題到抽象數學問題和解決方法實現的過程。上面的每一步都很重要。也怪不得我的演算法老師說,學完這個課程,你們如果能記住提出問題、分析問題、解決問題、驗證問題這個思想就足夠了。
拓撲排序演算法
對許多資料結構教材實在不滿意,至少我是看不懂 至於拓撲排序演算法,教材上那些偽 真真教人頭暈。只寫了幾個struct結構,我根本看不出這是鄰接表。如果給出乙個清晰明了的圖,一切不就簡單了?總之,關鍵就是建立乙個鄰接表。然後利用這個表進行拓撲排序。邊表結點宣告 typedef struct edgen...
拓撲排序演算法
對乙個 有向無環圖 directed acyclic graph簡稱dag g進行拓撲排序,是將g中所有頂點排成乙個線性序列,使得圖中任意一對頂點u和v,乙個有向無環圖的拓撲序列不是唯一的 進行拓撲排序的演算法並不複雜 1 在有向圖中選乙個沒有前驅 入度為0 的頂點且輸出之 2 從圖中刪除該頂點及它...
演算法 拓撲排序
定義 應用 有向圖來表示,圖中的頂點代表活動 子工程 圖中的有向邊代表活動的先後關係,即有向邊的起點的活動是終點活動的前序活動,只有當起點活動完成之後,其終點活動才能進行。通常,我們把這種頂點表示活動 邊表示活動間先後關係的有向圖稱做頂點活動網 activity on vertex network ...