有向無環圖
有向圖是由頂點
和有向邊
組成的,有向邊由上游點和下游點組成,比如(u,v)表示一個有向邊,其中u就是該有向邊的上游點,v就是該有向邊的下游點,入度
是指一個頂點作為下游點所在有向邊的個數,比如下圖中,頂點1的入度是0,頂點3的入度是1,頂點6的入度是2,出度
是指一個頂點作為上游點所在有向邊的個數,比如頂點1的出度是1,頂點3的出度是2.有向無環圖就是不存在環形的有向圖,即不存在任何一個從某個頂點出發,經過一條或者多條邊后,重新又回到了出發點的路徑。
有向圖的拓撲排序
是指這樣一個線性序列:如果(u,v)是有向無環圖中的一條邊,那么在線性序列中,u必須出現在v之前。利用拓撲排序所產生的線性序列不一定是唯一的。比如下圖的拓撲順序可以是:9->10->2->1->3->5->4->6->11->12->7->8->13->14
,也可以是1->3->2->4->5->6->9->10->11->12->7->8->13->14
.
那么如何給出一個有向無環的拓撲排序?
偽代碼如下:
輸入:G:一個頂點編號為從1~n的有向無環圖;
輸出:關於該圖的一個拓撲序列;
步驟:
1.構造一個入度數組,in-degree,根據G,填充該入度數組;
2.將入度數組中的每個入度值為0的頂點,壓入next棧;
3.只要next不為空,執行如下操作:
A.從next中彈出一個頂點u
B.將u添加到線性序列的末尾處
C.對於每個與u鄰接的下游點v:
i.令in-dgree[v]的值自減1
ii. 如果in-degree[v]=0,將v壓入next棧中
4.返回線性序列
該偽代碼的核心思想就是移除入度為0的頂點,以及其所在的有向邊,與該點鄰接的下游點的入度均減一,那么剩下的圖仍然是有向無環圖,反復操作,就得到了線性序列。比如下圖,移除1后,3就變成了入度值為0的頂點,故再移除3,同理,移除5,到了6,但是6的入度值此時為1,不為0,所以可以再移除2,移除4...等等。
class Program
{
public static void Main()
{
var a = new Dictionary<int, List<int>> { [0] = new List<int> { 1 } };
var graph = new Dictionary<int, List<int>>
{
[1] = new List<int>{ 3 },[2] = new List<int>{4},[3] = new List<int>{4,5},
[4] = new List<int>{ 6 },[5] = new List<int>{ 6 },
[6] = new List<int>{ 7, 11 },
[7] = new List<int>{ 8 },[8] = new List<int>{13},
[9] = new List<int>{ 10 },
[10] = new List<int>{ 11 }, [11] = new List<int>{ 12 },
[12] = new List<int>{ 13 },
[13] = new List<int>{14},[14]=new List<int>()
};
var result = TopoloGicalSort(graph);
foreach (var re in result)
Console.Write(re + "->");
}
/// <summary>
/// 用字典的形式來表示有向無環形圖
/// 其中鍵是每個頂點,值是該頂點傳向的頂點
/// </summary>
/// <param name="graph"></param>
public static List<int> TopoloGicalSort(Dictionary<int,List<int>>graph)
{
var result = new List<int>();
var inDegree = new Dictionary<int, int>();
//填充inDegree入度數組
foreach(var kv in graph)
{
if (!inDegree.ContainsKey(kv.Key))
{
inDegree.Add(kv.Key, 0);
}
foreach(var verteice in kv.Value)
{
if (!inDegree.ContainsKey(verteice))
{
inDegree.Add(verteice, 1);
}
else
{
inDegree[verteice] += 1;
}
}
}
var next = new Stack<int>();//存放所有入度為0的頂點的棧
//next初始化
foreach(var kv in inDegree)
{
if (kv.Value == 0)
{
next.Push(kv.Key);
}
}
while (next.Count != 0)
{
var verticeTemp = next.Pop();
result.Add(verticeTemp);
foreach (var vertice in graph[verticeTemp])
{
inDegree[vertice] -= 1;
if (inDegree[vertice] == 0)
next.Push(vertice);
}
}
return result;
}
}