• 拓扑排序算法的一个应用


    在Visual Studio .NET中,一个解决方案可以包含多个项目,一个项目可以引用若干其它项目。编译的时候,VS会自动确定每个项目的编译顺序。VS究竟是如何计算出这个顺序的呢?

    如果学习过数据结构,可以很容易回答出这个问题:拓扑排序(Topological Sort)。

    什么是拓扑排序?让我们来温习一下。百度百科上的介绍如下:
    对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若<u,v> ∈E(G),则u在线性序列中出现在v之前

    上述介绍抽象,不如用实际案例来解释一下。假如在VS中创建一个MVC的解决方案XMedia,该解决方案包含的项目,以及项目之间的引用关系如下表所示:   

    项目

    引用

    XMedia

    XMedia.Controllers、XMedia.Models、XMedia.Logics、XMedia.Commons

    XMedia.Controllers

    XMedia.Models、XMedia.Logics、XMedia.Commons

    XMedia.Models

     

    XMedia.Logics

    XMedia.Models、XMedia.Commons

    XMedia.Commons

     

    项目之间的引用关系,是一种依赖关系。如果项目A引用项目B,则表示A依赖B。所以,必须先编译项目B,再编译项目A。

    根据经验,我们可以得出上述项目的编译顺序依次是:XMedia.Commons、XMedia.Models、XMedia.Logics、XMedia.Controllers、XMedia。当然,也可以把前两项对调一下。

    项目和引用关系构成了一张有向图图,项目相当于有向图中的顶点(Vertex),引用关系相当于有向图中的边(Edge),而项目的编译顺序就是一个拓扑序列,产生该序列的算法称为拓扑排序算法。

    以下是项目引用关系的有向图展示:

    拓扑排序算法的简要描述:
    (1) 从有向图中选择一个出度为0的顶点并且输出它。
    (2) 从图中删去该顶点,并且删去该顶点的所有边。
    (3) 重复上述两步,直到剩余的图中没有出度为0的顶点。

    按照上述算法,运行过程演示如下:

    第一步 选择 XMedia.Commons节点


    第二步 选择XMedia.Models节点


    第三步 选择XMedia.Logics节点


    第四步 选择XMedia.Controllers节点


    第五步 选择XMedia节点

    接下来我们用C#实现代码实现这个算法。

    由于拓扑排序是一个应用很多的算法,所以,我们将实现一个通用的排序算法。在这个通用的算法中,我们将顶点之间的关系作为依赖关系。代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleApplication1 {
        /// <summary>
        /// 拓扑排序类。
        /// </summary>
        public class TopologicSort {
            /// <summary>
            /// 拓扑顺序。
            /// </summary>
            /// <typeparam name="TKey">节点的键值类型。</typeparam>
            /// <param name="nodes">一组节点。</param>
            /// <returns>拓扑序列。</returns>
            /// <exception cref="InvalidOperationException">如果存在双向引用或循环引用,则抛出该异常。</exception>
            public IEnumerable<string> OrderBy(IEnumerable<TopologicNode> nodes) {
                if (nodes == null) yield break;
    
                //复制一份,便于操作
                List<TopologicNode> list = new List<TopologicNode>();
                foreach (var item in nodes) {
                    TopologicNode node = new TopologicNode() { Key = item.Key };
                    if (item.Dependences != null)
                        node.Dependences = new List<string>(item.Dependences);
                    list.Add(node);
                }
    
                while (list.Count > 0) {
                    //查找依赖项为空的节点
                    var item = list.FirstOrDefault(c => c.Dependences == null || c.Dependences.Count == 0);
                    if (item != null) {
                        yield return item.Key;
    
                        //移除用过的节点,以及与其相关的依赖关系
                        list.Remove(item);
                        foreach (var otherNode in list) {
                            if (otherNode.Dependences != null)
                                otherNode.Dependences.Remove(item.Key);
                        }
                    } else if (list.Count > 0) {
                        //如果发现有向环,则抛出异常
                        throw new InvalidOperationException("存在双向引用或循环引用。");
                    }
                }
            }
        }
    
        /// <summary>
        /// 拓扑节点类。
        /// </summary>
        public class TopologicNode {
            /// <summary>
            /// 获取或设置节点的键值。
            /// </summary>
            public string Key { get; set; }
    
            /// <summary>
            /// 获取或设置依赖节点的键值列表。
            /// </summary>
            public List<string> Dependences { get; set; }
        }
    }


     测试代码如下:

    using System;
    using System.Collections.Generic;
    
    namespace ConsoleApplication1 {
        class Program {
            static void Main(string[] args) {
                List<TopologicNode> nodes = new List<TopologicNode>()
                {
                    new TopologicNode(){ Key = "XMedia", 
                        Dependences = new List<string>(){ "XMedia.Controllers", "XMedia.Models", "XMedia.Logics", "XMedia.Commons" } },
                    new TopologicNode(){ Key = "XMedia.Controllers",
                        Dependences = new List<string>(){"XMedia.Models","XMedia.Logics","XMedia.Commons"}},
                    new TopologicNode(){ Key = "XMedia.Logics", 
                        Dependences = new List<string>(){ "XMedia.Models","XMedia.Commons"}},
                    new TopologicNode(){ Key = "XMedia.Models" },
                    new TopologicNode(){ Key = "XMedia.Commons" }
                };
    
                //输出拓扑排序的结果
                TopologicSort sort = new TopologicSort();
                foreach (var key in sort.OrderBy(nodes)) {
                    Console.WriteLine(key);
                }
                Console.ReadLine();
            }
        }
    }

    运行结果如下图所示:

  • 相关阅读:
    存储过程
    sdsdsd
    sdsdd
    sdsd
    sdasd
    mysql触发
    c#连接mysql答题步骤
    c#mysql数据库
    nginx
    linux如何查看端口被何进程占用
  • 原文地址:https://www.cnblogs.com/lavezhang/p/2499017.html
Copyright © 2020-2023  润新知