数据结构---图---拓扑排序---6

拓扑排序主要解决一个工程能否顺序进行的问题。

无环图:图中没有回路

在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,我们称为AOV网(Activity On Vertex Network)。

AOV网中的弧表示活动之间存在的某种制约关系(也可以理解活动要分先后顺序),AOV网中不能有回路。

设G=(V,E)是一个具有n个顶点的有向图,V中的顶点序列v1,v2,…vn,满足若从顶点vi到vj有一条路径,则在顶点序列中顶点vi必在顶点vj之前。则我们称这样的顶点序列为一个拓扑序列。

在这里插入图片描述
在这里插入图片描述
拓扑排序:就是对一个有向图构造拓扑序列的过程。

构造时会有两个结果,如果此网的全部顶点都被输出,则说明它是不存在环(回路)的AOV网;如果输出顶点数少了,哪怕少了一个,也说明这个网存在环(回路),不是AOV网。

一个不存在回路的AOV网,我们可以将它应用在各种各样的工程或项目的流程图中,满足各种应用场景的需要,所以实现拓扑排序的算法就很有价值了。

拓扑排序算法:

对AOV网进行拓扑排序的基本思路是:从AOV网中选择一个入度为0的顶点输出,然后删去此顶点,并删除以此顶点为尾的弧,继续重复此步骤,直到输出全部顶点或者AOV网中不存在入度为0的顶点为止。

在这里插入图片描述
在这里插入图片描述

C# 代码:可以只看最后的拓扑排序部分

//测试数据:就是上面的图中的数据,可以把VertexNode类放到外面,就不用嵌套写了,也可以用简化语法using之类的
 AdjacencyList<int>.VertexNode<int>[] vertexArr = new AdjacencyList<int>.VertexNode<int>[] {

            new AdjacencyList<int>.VertexNode<int>() { data = 0,inDegree=0, firstEdge=new AdjacencyList<int>.EdgeNode(){  adjvex=11,next=new AdjacencyList<int>.EdgeNode(){adjvex=5,next=new AdjacencyList<int>.EdgeNode(){ adjvex=4} } } },
            new AdjacencyList<int>.VertexNode<int>() { data = 1,inDegree=0, firstEdge=new AdjacencyList<int>.EdgeNode(){  adjvex=8,next=new AdjacencyList<int>.EdgeNode(){adjvex=4,next=new AdjacencyList<int>.EdgeNode(){ adjvex=2} } } },
            new AdjacencyList<int>.VertexNode<int>() { data = 2,inDegree=2, firstEdge=new AdjacencyList<int>.EdgeNode(){  adjvex=9,next=new AdjacencyList<int>.EdgeNode(){adjvex=6,next=new AdjacencyList<int>.EdgeNode(){ adjvex=5} } } },
            new AdjacencyList<int>.VertexNode<int>() { data = 3,inDegree=0, firstEdge=new AdjacencyList<int>.EdgeNode(){  adjvex=13,next=new AdjacencyList<int>.EdgeNode(){adjvex=2 } } },
            new AdjacencyList<int>.VertexNode<int>() { data = 4,inDegree=2, firstEdge=new AdjacencyList<int>.EdgeNode(){  adjvex=7} },
            new AdjacencyList<int>.VertexNode<int>() { data = 5,inDegree=3, firstEdge=new AdjacencyList<int>.EdgeNode(){  adjvex=12,next=new AdjacencyList<int>.EdgeNode(){adjvex=8 } } },
            new AdjacencyList<int>.VertexNode<int>() { data = 6,inDegree=1, firstEdge=new AdjacencyList<int>.EdgeNode(){  adjvex=5} },
            new AdjacencyList<int>.VertexNode<int>() { data = 7,inDegree=2},
            new AdjacencyList<int>.VertexNode<int>() { data = 8,inDegree=2, firstEdge=new AdjacencyList<int>.EdgeNode(){  adjvex=7} },
            new AdjacencyList<int>.VertexNode<int>() { data = 9,inDegree=1, firstEdge=new AdjacencyList<int>.EdgeNode(){  adjvex=11,next=new AdjacencyList<int>.EdgeNode(){adjvex=10 } } },
            new AdjacencyList<int>.VertexNode<int>() { data = 10,inDegree=1, firstEdge=new AdjacencyList<int>.EdgeNode(){  adjvex=13} },
            new AdjacencyList<int>.VertexNode<int>() { data = 11,inDegree=2},
            new AdjacencyList<int>.VertexNode<int>() { data = 12,inDegree=1, firstEdge=new AdjacencyList<int>.EdgeNode(){  adjvex=9} },
            new AdjacencyList<int>.VertexNode<int>() { data = 13,inDegree=2},
        };

        AdjacencyList<int> al = new AdjacencyList<int>(vertexArr);
        al.TopologicalSort();
        //输出结果:->3->1->2->6->9->10->13->0->4->5->8->7->12->11
        //序列可能不止一个,只要输出了所有结点就是对的。

源码:拓扑排序在最后

/// <summary>
/// 泛型邻接表:一维数组 + 链表(数组和链表自行提供)
/// </summary>
public class AdjacencyList<T>
{
    /// <summary>
    /// 边表结点
    /// </summary>
    public class EdgeNode
    {
        public int adjvex;      //邻接点域,存储该顶点对应的下标
        public int weight;      //用于存储权值,对于非网图可以不需要(关键路径需要权值)
        public EdgeNode next;   //链域,指向下一个邻接点
    }

    /// <summary>
    /// 顶点表结点
    /// </summary>
    public class VertexNode<T>
    {
        public int inDegree;            //入度:拓扑排序用
        public T data;                  //顶点域,存储顶点信息
        public EdgeNode firstEdge;      //边表头指针
    }

    private VertexNode<T>[] adjacencyArr;   //顶点表

    /// <summary>
    /// 说明:没有按书上写,需要的数据自行提供
    /// </summary>
    /// <param name="vertexData">顶点数据</param>    
    public AdjacencyList(VertexNode<T>[] vertexData)
    {
        adjacencyArr = vertexData;
    }

    private bool[] visited;

    #region 操作

    /// <summary>
    /// 邻接表的深度优先遍历
    /// </summary>   
    public void DFSTraverse()
    {
        visited = new bool[adjacencyArr.Length];

        for (int i = 0; i < adjacencyArr.Length; i++)
        {
            visited[i] = false; //初始化所有顶点都是未访问的状态
        }

        for (int i = 0; i < adjacencyArr.Length; i++)
        {
            //对未访问过的顶点调用DFS,若是连通图,只会执行一次
            if (!visited[i])
            {
                DFS(i);
            }
        }
    }

    /// <summary>
    /// 邻接表的深度优先递归算法
    /// </summary>
    /// <param name="gl"></param>
    /// <param name="i"></param>
    private void DFS(int i)
    {
        visited[i] = true;

        //这里对顶点的操作,这里简单的打印
        Debug.Log(adjacencyArr[i].data);

        EdgeNode temp = adjacencyArr[i].firstEdge;

        while (temp != null)
        {
            if (!visited[temp.adjvex])
                DFS(temp.adjvex);
            temp = temp.next;
        }
    }

    /// <summary>
    /// 邻接表:广度优先遍历
    /// </summary>
    /// <param name="gl"></param>
    public void BFSTraverse()
    {
        visited = new bool[adjacencyArr.Length];

        Queue<int> queue = new Queue<int>();  //初始化辅助队列

        EdgeNode p;

        for (int i = 0; i < adjacencyArr.Length; i++)
        {
            visited[i] = false;
        }

        for (int i = 0; i < adjacencyArr.Length; i++)   //对每一个顶点做循环
        {
            if (!visited[i])            //若是未访问过就处理
            {
                visited[i] = true;      //设置当前顶点访问过

                //这里对顶点的操作,这里简单的打印
                Debug.Log(adjacencyArr[i].data);

                queue.Enqueue(i);       //将此顶点入队列

                while (queue.Count > 0) //当前队列有元素
                {
                    i = queue.Dequeue();    //出队列

                    p = adjacencyArr[i].firstEdge;   //找到当前顶点边表链表头指针

                    while (p != null)
                    {
                        //此顶点未访问过
                        if (!visited[p.adjvex])
                        {
                            visited[p.adjvex] = true;

                            //这里对顶点的操作,这里简单的打印
                            Debug.Log(adjacencyArr[p.adjvex].data);

                            queue.Enqueue(p.adjvex);        //将此顶点入队列
                        }

                        p = p.next;     //指针指向下一个邻接点
                    }
                }
            }
        }
    }

    /// <summary>
    /// 拓扑排序:需要一个栈辅助,存储处理过程中入度为0的顶点
    /// 目的是为了避免么个查找时都要去遍历顶点表找有没有入度为0的顶点
    /// </summary>   
    /// <returns>无回路返回true,有回路返回false</returns>
    public bool TopologicalSort()
    {
        EdgeNode node;
        int k, topIndex;
        int count = 0;          //用于统计输出顶点的个数                     
        Stack<int> stack = new Stack<int>(adjacencyArr.Length);  //建栈存储入度为0的顶点
        for (int i = 0; i < adjacencyArr.Length; i++)
        {
            if (adjacencyArr[i].inDegree == 0)
            {
                stack.Push(i);
            }
        }

        string result = string.Empty;

        while (stack.Count != 0)
        {
            topIndex = stack.Pop();       //出栈
             
            result += "->" + adjacencyArr[topIndex].data;        //打印此顶点

            count++;        //统计输出顶点数

            for (node = adjacencyArr[topIndex].firstEdge; node != null; node = node.next)
            {
                //对此顶点弧表遍历
                k = node.adjvex;
                if ((--adjacencyArr[k].inDegree) == 0)   //将k号顶点邻接点的入度减1
                {
                    stack.Push(k);          //若为0则入栈,以便下次循环输出
                }
            }
        }

        Debug.Log(result);      //打印结果

        if (count < adjacencyArr.Length) return false;     //如果count小于顶点数,说明存在环
        else return true;
    }
    #endregion
}

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值