图的排序和遍历

??基础排序和查找算法完结后就进入了图这一章节。

整理数据结构一直参考的是《数据结构与算法c#语言描述》这本书,是图灵系列丛书,相信学计算机的同学都很欣赏这个系列的书,但是整理到这个地步却发现两个编写不合理的地方。第一个是集合操作,细看会发现给的代码是有问题的,不能运用到实际中;第二个是本篇文章将要记录的有向图的拓扑排序。也许是翻译的问题,也或许是作者故意留下让读者去探索~~~,so ,whatever,本书对算法思想的解释非常清晰易懂的。

图的构造需要顶点和边,顶点类定义如下:

class Vertex
{
    public bool wasVisited;
    public string value;

    public Vertex(string value)
    {
        this.value = value;
        wasVisited = false;

    }
}

wasVisited变量在遍历的时候会用到。

边的表示要借助邻接矩阵,纵坐标表示起点,横坐标表示终点。如matrix[3,4]表示顶点3到顶点4的边。

拓扑排序

??拓扑排序是对图的很好的应用,拓扑排序会把有向图中的顶点序列按照指定顺序显示出来。如下图(好久没有用EA画图了,略搓~~~):

用拓扑排序算法就可以得出他们之前正确的顺序。

拓扑排序算法:

  1. 找到一个没有后继顶点的顶点
  2. 把此顶点添加到顶点列表内
  3. 从图中移除此顶点
  4. 重复步骤1知道把所有顶点从图中移除掉。

拓扑排序算法的实现

找到没有后继顶点的顶点

    /// <summary>
    /// find the vertex that have no succeed vertex
    /// </summary>
    /// <returns>the row in the Road_Matrix of this vertex</returns>
    public int FindNoSuccessors()
    {
        bool HaveSuccessor;
        for (int row = 0; row < Num_Vertex; row ++)
        {
            HaveSuccessor = false;
            for (int col = 0; col < Num_Vertex; col++)
            {
                if (Road_Matrix[row,col]>0)//如果该行所有值为0则表明该行所对应的节点没有后继节点
                {
                    HaveSuccessor = true;
                    break;
                }
            }
            if (!HaveSuccessor)
            {
                return row;
            }
        }
        return -1;
    }

写了注释,应该好理解。

删除顶点

```
/// <summary>
/// delete vertex
/// </summary>
/// <param name="v">the row in the Road_Matrix of this vertex</param>
public void DelVertex(int v )
{
    if (v!=Num_Vertex-1)
    {
        movetimes++;
        for (int i = v; i < VertexCount - 1; i++)//将节点从节点库中删除
        {
            Vertices[i] = Vertices[i + 1];
        }
        for (int row = v; row < VertexCount - 1; row++)//将该行以下的行上移
        {
            MoveRow(row, VertexCount);
        }
        for (int delcol = 0; delcol < Num_Vertex; delcol++)//将移动完后的空出的一行置空
        {
            Road_Matrix[Num_Vertex - 1 - movetimes + 1, delcol] = 0;
        }

        for (int col = v; col < VertexCount - 1; col++)//将该列以后的列前移
        {
            MoveCol(col, VertexCount);
        }
        for (int delrow = 0; delrow < Num_Vertex; delrow++)//将移动完后空出的那列置空
        {
            Road_Matrix[delrow, Num_Vertex - 1 - movetimes + 1] = 0;
        }
    }
    VertexCount--;
}

public void MoveRow(int row, int  count)
{
    for (int col = 0; col <= count - 1; col++)
    {
        Road_Matrix[row, col] = Road_Matrix[row + 1, col];
    }
}
public void MoveCol(int col, int count)
{
    for (int row = 0; row <= count - 1; row++)
    {
        Road_Matrix[row, col] = Road_Matrix[row, col + 1];
    }
}
```

这里必须要说一下了,看过书的同学会发现怎么跟书上的代码不一样,这就是书中有误导的地方。如果按照书上的代码,很明显就会发现,添加顶点的顺序直接影响到了最后结果排出的顺序,而且由于在删除顶点函数中没有置空已经移动过的行列会使按照书上代码运行得不到书上所粘贴的控制台输出结果。所以我改了书上的代码,但是还是要谢谢书中提供的思想。更改过后的代码可以按照随意添加节点,然后添加边,经过拓扑排序得出正确的顺序。

拓扑排序

??在这个函数中在输出到结果序列之前,先得到即将输出到结果序列的顶点到起始点的距离,然后存在一个过渡数组中,在按照距离由远及近放入结果序列中。

代码如下:

/// <summary>
/// Topology sort
/// </summary>
public void TopologySort()
{
    int[] resultbridge = new int[Num_Vertex];//在存储得出的初步序列和每个节点对应的距离,索引为节点在辅助矩阵中的
    //行数,值为对应的距离
    Stack<string> result = new Stack<string>();//存储最后排好序的节点

    while (VertexCount > 0)
    {
        int currentVertex = FindNoSuccessors();

        if (currentVertex==-1)
        {
            Console.WriteLine("error:graph has cycles");
            return;
        }

        dis = 0;
        GetDistance(currentVertex);

        foreach (var item in map.Keys)//要对应的放入过渡矩阵必须将对应的距离值放入对应的索引中,但是经过删除后
            //verteics中的索引和值已经变化了,这时map就起作用了,用于找到对应的索引值
        {
            if ((string)map[item]==Vertices[currentVertex].value)
            {
                resultbridge[(int)item] = dis;
            }
        }

        DelVertex(currentVertex);

    }
    while (result.Count < Num_Vertex)//放入结果栈,距离远的在下
        result.Push((string)map[GetMaxdis(resultbridge)]);
    #region test GetDistance
    //for (int i = 0; i < resultbridge.Length; i++)
    //{
    //    Console.Write(i + "   " + resultbridge[i]);
    //    Console.WriteLine();
    //}
    #endregion

    Console.Write("Topological sorting order : ");
    while (result.Count > 0)
    {
        Console.Write(result.Pop() + " ");
    }
}
public int GetMaxdis(int[] a)//取得一个数组中最大值得索引
{
    for (int i = 0; i < a.Length; i++)
    {
        if (a[i]==a.Max())
        {
            a[i] = -1;
            return i;
        }
    }
    return -1;
}

深度优先遍历

??沿着一条路径从开始顶点到达最后顶点,然后原路返回,并且沿着下一条路径达到最后的顶点,如此继续走完所有的路径。

代码如下:

/// <summary>
/// 深度优先遍历
/// </summary>
public void DepthFirstSearch()
{
    Stack<int> result = new Stack<int>();
    Vertices[0].wasVisited = true;
    ShowVertex(0);
    result.Push(0);
    int v;
    while (result.Count > 0)
    {
        v = GetSuccessorUnvisited(result.Peek());
        if (v == -1)
        {
            result.Pop();
        }
        else
        {
            Vertices[v].wasVisited = true;
            ShowVertex(v);
            result.Push(v);
        }
    }
    for (int i = 0; i < Num_Vertex; i++)
    {
        Vertices[i].wasVisited = false;
    }
}

其中的GetSuccessorUnvisited()方法是得到后继顶点。

广度优先

??从第一个顶点开始尝试访问所有可能在第一个顶点附近的顶点,然后访问所有可能在第二个顶点附近的顶点,直到全部访问完。这是一种逐层进行的访问方式。

代码如下:

/// <summary>
/// 广度优先遍历
/// </summary>
public void BreadFirstSearch()
{
    Queue<int> result = new Queue<int>();
    Vertices[0].wasVisited = true;
    ShowVertex(0);
    result.Enqueue(0);
    int vert1, vert2;
    while (result.Count > 0)
    {
        vert1 = result.Dequeue();
        vert2 = GetSuccessorUnvisited(vert1);
        while (-1 != vert2)
        {
            Vertices[vert2].wasVisited = true;
            ShowVertex(vert2);
            result.Enqueue(vert2);
            vert2 = GetSuccessorUnvisited(vert1);
        }
    }
    for (int i = 0; i < Num_Vertex; i++)
    {
        Vertices[i].wasVisited = false;
    }
}

最小生成树

??覆盖每个顶点所必须的最少数量的构造边,之所以说它是树因为结果图是非循环的。

代码和深度优先差不多:

/// <summary>
/// 最小生成树算法
/// </summary>
public void FindMinTree()
{
    Stack<int> result = new Stack<int>();
    Vertices[0].wasVisited = true;
    result.Push(0);
    int v;
    while (result.Count > 0)
    {
        v = GetSuccessorUnvisited(result.Peek());
        if (v == -1)
        {
            result.Pop();
        }
        else
        {
            Vertices[v].wasVisited = true;
            ShowVertex(result.Peek());
            ShowVertex(v);
            Console.Write("|");
            result.Push(v);
        }
    }
    for (int i = 0; i < Num_Vertex; i++)
    {
        Vertices[i].wasVisited = false;
    }
}

date: 2013-04-29 16:59:40

原文地址:https://www.cnblogs.com/erdao/p/8419785.html

时间: 2024-08-30 17:04:01

图的排序和遍历的相关文章

模板 - 图论 - 图的存储和遍历

链式前向星法存的带边权的图,(尤其在多组数据时)时间效率比vector略高且节省空间,缺点是不容易对一个点的出边进行排序去重,当平行边无所谓时选择这个方法是非常明智的.链式前向星法存图的最大的问题是要记得给反向边预留空间. 图的存储和遍历,在图中搜索树的父子关系其实一般不是很重要.注意下面的代码是没有对vis进行清空的,因为其实并不是每次搜索前都会需要清空,有时候有一些其他的操作(特别是有向图).需要管边权的去找dijkstra算法就好了. struct Graph { static const

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

拓扑排序 拓扑排序 在实际应用中,有向图的边可以看做是顶点之间制约关系的描述.把顶点看作是一个个任务,则对于有向边<Vi,Vj>表明任务Vj的完成需等到任务Vi完成之后,也就是说任务Vi先于任务Vj完成.对于一个有向图,找出一个顶点序列,且序列满足:若顶点Vi和Vj之间有一条边<Vi,Vj>,则在此序列中顶点Vi必在顶点Vj之前.这样的一个序列就称为有向图的拓扑序列(topological order). 步骤 从有向图中选取一个没有前驱(入度为0)的顶点输出. 删除图中所有以它为

数据结构实验报告-实验四 图的构造与遍历

实验四   图的构造与遍历   l  实验目的 1.图的的定义和遍历 (1)掌握图的邻接矩阵.邻接表的表示方法. (2)掌握建立图的邻接矩阵的算法. (3)掌握建立图的邻接表的算法. (4)加深对图的理解,逐步培养解决实际问题的能力. l  实验内容 1.图的定义和遍历 (一)基础题 1.编写图基本操作函数: (1)CreateALGraph(ALGraph &G) 建立无向图的邻接表表示: (2)LocateVex(ALGraph &G,char v)图查找信息: (3)DFSTrave

图形化排序算法比较:快速排序、插入排序、选择排序、冒泡排序

图形化排序算法比较:快速排序.插入排序.选择排序.冒泡排序

HDU4857——逃生(反向建图+拓扑排序)

逃生 Description 糟糕的事情发生啦,现在大家都忙着逃命.但是逃命的通道很窄,大家只能排成一行. 现在有n个人,从1标号到n.同时有一些奇怪的约束条件,每个都形如:a必须在b之前.同时,社会是不平等的,这些人有的穷有的富.1号最富,2号第二富,以此类推.有钱人就贿赂负责人,所以他们有一些好处.负责人现在可以安排大家排队的顺序,由于收了好处,所以他要让1号尽量靠前,如果此时还有多种情况,就再让2号尽量靠前,如果还有多种情况,就让3号尽量靠前,以此类推.那么你就要安排大家的顺序.我们保证一

邻接矩阵(以顶点为中心),比较稀疏时,采用邻接表;图的两种遍历(邻接矩阵实现)

对于边比较稠密的图,可以采用邻接矩阵(以顶点为中心)的方式表示,而边比较稀疏时,采用邻接表的结构更合适.两种都不能直观表达哪两个点相连或者最短路径是什么. 深度优先遍历类似于树的先根序遍历.与树不同的是,它需要对已经访问过的节点添加标记以免被重复遍历. public class Depth { /** * 对k号节点深度遍历 * @param a * @param color * @param k 节点 */ public static void depthTraversal(int[][] a

数据结构算法之图的存储与遍历(Java)

一:图的分类 1:无向图 即两个顶点之间没有明确的指向关系,只有一条边相连,例如,A顶点和B顶点之间可以表示为 <A, B> 也可以表示为<B, A>,如下所示 2:有向图 顶点之间是有方向性的,例如A和B顶点之间,A指向了B,B也指向了A,两者是不同的,如果给边赋予权重,那么这种异同便更加显著了 =============================================================================================

图的两种遍历-DFS&amp;BFS

DFS和BFS在图中的应用: 图连通性判定:路径的存在性:图中是否存在环:求图的最小生成树:求图的关键路径:求图的拓扑排序. DFS:简单的说,先一直往深处走,直到不能再深了,再从另一条路开始往深处走,直到所有路都走完: struct node { int next; //E[i].next指向图中与i同父的下一个结点 int to; //E[i].to指向图中i的子结点 }E[110]; int N; int fa[110]; //记录各点的父结点 bool vis[110]; //记录这个点

图——拓扑排序(uva10305)

John has n tasks to do. Unfortunately, the tasks are not independent and the execution of one task is only possible if other tasks have already been executed. Input The input will consist of several instances of the problem. Each instance begins with