有向无环图

有向无环图

有向图是由顶点有向边组成的,有向边由上游点和下游点组成,比如(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;
        }
    }

posted @ 2022-01-02 13:54  JohnYang819  阅读(1586)  评论(0编辑  收藏  举报