有向無環圖


有向無環圖

有向圖是由頂點有向邊組成的,有向邊由上游點和下游點組成,比如(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;
        }
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM