關於貪心算法介紹:
http://blog.csdn.net/mind_v/article/details/72956707
拓撲排序
問題描述
一個複雜的工程,經常可以分解成一組簡單一些的任務,這些任務完成了,整個工程就完成了。例如汽車組裝問題可以分解成:底盤安裝、車軸安裝、車輪安裝、座位安裝、噴漆、剎車安裝、車門安裝等。但是這些任務之間有個先後順序,例如車軸安裝之前先要進行底盤安裝。
類似的問題,可以將任務和人物的先後順序用有向圖表示——稱爲頂點活動網絡,頂點代表一個任務,有向邊(i,j)代表任務i要在任務j之前完成。下圖就是一個工程,它有6個任務,邊(1,3)表示任務1要在任務3開始前完成,邊(4,6)表示任務4要在任務6之前完成。
這樣就形成了一個任務序列,對有向圖的任意一條邊(i,j),在這個序列中,任務i一定出現在任務j之前,具有這種性質的序列叫做拓撲序列(topological order),根據有向圖建立拓撲序列的過程稱爲拓撲排序(topological sorting)。
對於這樣一個有向圖有若干個拓撲序列,如1-2-3-4-5-6,1-3-2-4-5-6,2-1-3-4-5-6等。
貪心求解
我們可以通過從左到右分步構造拓撲序列,每一步選擇一個新頂點加入到序列中。選擇新頂點的貪心準則:從剩餘的頂點中選擇一個頂點w,所有鄰接於它的頂點都在序列中。
算法步驟:
令n表示有向圖的頂點數
令theOrder表示空虛列
while(true)
{
令w是任意一個沒有入邊(v,w)的頂點,其中v不在theOrder中
如果沒有這樣的頂點w,算法終止
把w加入到theOrder的尾部
}
if(theOrder的頂點數小於n)
算法失敗
else
theOrder是一個拓撲序列
針對上圖,使用這樣一個算法:
從theOrder是一個空序列開始,第一步選擇插入theOrder序列的第一個頂點,頂點1和2都滿足貪心準則。若選擇1,則theOrder{1};第二步插入第二個頂點,這時有兩個個候選頂點2,3。若選擇3,則theOrder{1,3};第三步選擇第三個頂點,候選頂點只有2,則theOrder{1,3,2};第四步候選頂點爲4和5,如果選擇4,則theOrder{1,3,2,4};第五步只有一個候選頂點5,第六步最後一個頂點6,則theOrder{1,3,2,4,5,6}。
數據結構選擇:
用一個一維數組表示theOrder,存儲拓撲序列;用一個棧存儲可以加入theOrder的候選頂點; 用一個一維數組inDegree存儲每個頂點的實時入度,inDegree[j]表示不在theOrder中但鄰接於頂點j的數目。每次向theOrder中添加,一個頂點時,所有鄰接於此頂點的頂點j,theDegree[j]減1,當inDegree變成0時,表示頂點j成爲候選頂點。
C++實現
函數是圖的graph抽象類的成員函數,使用到的接口包括:
directed():確定圖是否是有向圖
numberOfVertices():返回圖的頂點數
vertexIterator類:創造一個頂點的迭代器,其next()成員返回鄰接於改頂點的下一個頂點。
arrayStack是用數組描述的棧,接口:
push(i):將i壓入棧
pop(),刪除棧頂元素
bool topologicalOrder(int *theOrder)
{//返回false,當且僅當有向圖沒有拓撲序列
//如果存在一個拓撲序列,則將頂點賦給theOrder[0,n-1]
//確定是有向圖
if (!directed())
return;
int n = numberOfVertices();
//計算入度
int *inDegree = new int[n + 1];
fill(inDegree + 1, inDegree + n + 1, 0);
for (int i = 1; i <= n; ++i)
{
vertexIterator<int> *ii = iterator(i);
int u;
if ((u = ii->next()) != 0)
inDegree[u]++;
}
//把入邊數加入到棧中
arrayStack<int> stack;
for (int i = 1; i <= n; ++i)
if (inDegree[i] == 0)
stack.push(i);
//生成拓撲序列
int j = 0;
while (!stack.empty())
{
int theVertex = stack.top();
stack.pop();
theOrder[j++] = theVertex;
//更新入邊數
vertexIterator<int> *iTheVertex = iterator(theVertex);
int u;
while ((u = iTheVertex->next()) != 0)
{
inDegree[u]--;
if (inDegree[u] == 0)
stack.push(u);
}
}
return (j == n);
}
複雜度分析:
計算入度的for循環的時間複雜度爲O(n2);生成拓撲序列的外部while循環的時間複雜度爲O(n),內層嵌套的while循環的時間複雜度爲O(n),所以總的時間複雜度爲O(n2)。故此算法的時間複雜度爲O(n2)。