python中判断无向图是否有环_数据结构与算法:17 图

545523d50c67838c0b255272e2dc5523.png

17 图

知识结构:

579f1ccfc01289ca16040c86793e7516.png
图1 知识结构

1. 图的基本概念与术语

1.1 图的定义

图由顶点集和边集组成,记为 。

  • 顶点集:顶点的有穷非空集合,记为。
  • 边集:顶点偶对的有穷集合,记为 。

边:

  • 无向边:
  • 有向边(弧): 始点称为弧尾,终点称为弧头。

例题:

de00ac47732c91f4c4657c4661381834.png
图2 图的表示

例题:

d10592f1244b80b6e1f3d2b40df348b5.png
图3 图的表示

1.2 图的分类

  • 有向图:由有向边构成。
  • 无向图:由无向边构成。

1.3 图中顶点数与边数的关系

(1)设顶点数为,边数为则:

  • 无向图:,的图称为无向完全图。
  • 有向图:,的图称为有向完全图。

(2)顶点的度

  • 无向图:与连接的边数。
  • 有向图:
    • :以为终点(弧头)的边数。
    • :以为起点(弧尾)的边数。

(3)度与边的关系

(4)正则图:所有顶点的度都相同的图

d05c624d73c94d0e871e96aa5f1a7d0f.png
图4 正则图

1.4 路径

(1)定义:图,若存在一个顶点序列 ,使得(无向图)或者(有向图)则称到存在一条路径。

(2)路径的长度:路径上边的数目。

(3)简单路径:若一条路径上除,可相同外,其余顶点均不相同的路径。

(4)简单回路:与相同的简单路径。

1.5 子图

设是一个图,若,且中的边所关联的顶点,则 也是一个图,并称其为的子图。

f557642ff03074453e514813ff0127f2.png
图5 图与子图

1.6 连通图与连通分量(无向图)

连通图:无向图的中,任何两个顶点都存在路径,则称为连通图。

连通分量:无向图的极大连通子图。

注:

  • 连通图的连通分量只有一个,为其本身。
  • 非连通图的连通分量不唯一。

例子:

9af37bd529185839223a0e80c1d82c73.png
图6 连通图与连通分量

1.7 强连通图与强连通分量(有向图)

强连通图:有向图的中,任何两个顶点都存在路径,则称为强连通图。

强连通分量:有向图的极大连通子图。

注:

  • 强连通图的强连通分量只有一个,为其本身。
  • 非强连通图的强连通分量不唯一。

例子:

bd824fc23d68c376ca07a773218384f9.png
图7 强连通图与强联通分量

1.8 网络

中的每条边都带有权值的图称为网络。

注:权一般具有实际意义,比如距离、消耗等。

例子:

a3adfcb91e5db73130270f56daf00e7e.png
图8 网络

2. 图的存储结构

2.1 顺序存储(邻接矩阵)

邻接矩阵:表示顶点之间相邻关系的矩阵

若图,则其邻接矩阵可表示为:

例子:无向图的存储

8205d808a73506aeacae9a5c4a3f40fe.png
图9 邻接矩阵存储无向图

例子:有向图的存储

a76e874f7169dadba976190289300a7d.png
图10 邻接矩阵存储有向图

若图为网络,则其邻接矩阵可表示为:

例子:网络的存储

110c0a7c925e997a7dad08274a8614aa.png
图11 邻接矩阵存储网络
d9514e5b87ec29127c1bdfd71232ca9a.png
图12 MGraph结构
using System;
namespace NonLinearStruct.Graph
{
    /// 
    /// 图抽象数据类型实现--利用邻接矩阵
    /// 
    public class MGraph
    {
        private readonly double[,] _adMatrix;//邻接矩阵
        private readonly string[] _vertexNameList;//存储图中各结点名字的数组

        /// 
        /// 获取图中结点的个数
        /// 
        public int VertexCount { get; }

        /// 
        /// 初始化MGraph类的新实例
        /// 
        /// 图中结点的个数
        public MGraph(int vCount){
            if (vCount <= 0)
                throw new ArgumentOutOfRangeException();

            VertexCount = vCount;
            _vertexNameList = new string[vCount];
            _adMatrix = new double[vCount, vCount];
        }
        
        /// 
        /// 获取或设置图中各结点的名称
        /// 
        /// 结点名称从零开始的索引
        /// 指定索引处结点的名称
        public string this[int index]
        {
            get
            {
                if (index 0 || index > VertexCount - 1)
                    throw new IndexOutOfRangeException();
                return _vertexNameList[index];
            }
            set
            {
                if (index 0 || index > VertexCount - 1)
                    throw new IndexOutOfRangeException();
                _vertexNameList[index] = value;
            }
        }
        
        private int GetIndex(string vertexName){
            //根据结点名字获取结点在数组中的下标
            int i;
            for (i = 0; i             {
                if (_vertexNameList[i] == vertexName)
                    break;
            }
            return i == VertexCount ? -1 : i;
        }
        
        /// 
        /// 给邻接矩阵赋值
        /// 
        /// 起始结点的名字
        /// 终止结点的名字
        /// 边上的权值或连接关系
        public void AddEdge(string startVertexName, string endVertexName, double weight = 1){
            int i = GetIndex(startVertexName);
            int j = GetIndex(endVertexName);
            if (i == -1 || j == -1)
                throw new Exception("图中不存在该边.");
            _adMatrix[i, j] = weight;
        }
    }
}

2.2 链式存储(邻接表)

邻接表:由顺序存储的顶点表和链式存储的边表构成的图的存储结构。

例子:无向图的存储

51cacaeddd1755f3081b9aefe8dd2ab8.png
图13 无向图的存储

例子:有向图的存储

c5aec3a81582302734ef5b65632482e3.png
图14 有向图的存储

例子:网络的存储

6052da2ac2117e7b62cd67cfb2007d5d.png
图15 网络的存储

边表结点的实现:

e0e7d3d94fbc0524f2d2f3b43a2d5a2d.png
图16 边表结点结构
949a52127109bb703ba3512c9042d9f2.png
图17 EdgeNode结构
using System;
namespace NonLinearStruct.Graph
{
    /// 
    /// 邻接表边表上的结点
    /// 
    public class EdgeNode
    {
        /// 
        /// 获取边终点在顶点数组中的位置
        /// 
        public int Index { get; }

        /// 
        /// 获取边上的权值
        /// 
        public double Weight { get; }

        /// 
        /// 获取或设置下一个邻接点
        /// 
        public EdgeNode Next { get; set; }

        /// 
        /// 初始化EdgeNode类的新实例
        /// 
        /// 边终点在顶点数组中的位置
        /// 边上的权值
        /// 下一个邻接点
        public EdgeNode(int index, double weight = 0.0, EdgeNode next = null){
            if (index 0)
                throw new ArgumentOutOfRangeException();

            Index = index;
            Weight = weight;
            Next = next;
        }
    }
}

顶点表结点的实现:

63b66996e94aff3a377a2710c55ad06c.png
图18 顶点表结点结构
639eccd53021bc9e113260f0c3ccb272.png
图19 VertextNode结构
namespace NonLinearStruct.Graph
{
    /// 
    /// 邻接表顶点表中的结点
    /// 
    public class VertexNode
    {
        /// 
        /// 获取或设置顶点的名字
        /// 
        public string VertexName { get; set; }

        /// 
        /// 获取或设置顶点是否被访问
        /// 
        public bool Visited { get; set; }

        /// 
        /// 获取或设置顶点的第一个邻接点
        /// 
        public EdgeNode FirstNode { get; set; }

        /// 
        /// 初始化VertexNode类的新实例
        /// 
        /// 顶点的名字
        /// 顶点的第一个邻接点
        public VertexNode(string vName, EdgeNode firstNode = null){
            VertexName = vName;
            Visited = false;
            FirstNode = firstNode;
        }
    }
}

图结构的实现

c3017fb38e92a9bafca3a1fcc564afce.png
图20 AdGraph结构
using System;
using System.Collections.Generic;
using System.Linq;
using LinearStruct;

namespace NonLinearStruct.Graph
{
    /// 
    /// 图抽象数据类型实现--利用邻接表
    /// 
    public class AdGraph
    {
        private readonly VertexNode[] _vertexList; //结点表

        /// 
        /// 获取图的结点数
        /// 
        public int VertexCount { get; }

        /// 
        /// 初始化AdGraph类的新实例
        /// 
        /// 图中结点的个数
        public AdGraph(int vCount){
            if (vCount <= 0)
                throw new ArgumentOutOfRangeException();

            VertexCount = vCount;
            _vertexList = new VertexNode[vCount];
        }

        /// 
        /// 获取或设置图中各结点的名称
        /// 
        /// 结点名称从零开始的索引
        /// 指定索引处结点的名称
        public string this[int index]
        {
            get
            {
                if (index 0 || index > VertexCount - 1)
                    throw new ArgumentOutOfRangeException();

                return _vertexList[index] == null
                    ? "NULL"
                    : _vertexList[index].VertexName;
            }
            set
            {
                if (index 0 || index > VertexCount - 1)
                    throw new ArgumentOutOfRangeException();

                if (_vertexList[index] == null)
                    _vertexList[index] = new VertexNode(value);
                else
                    _vertexList[index].VertexName = value;
            }
        }

        /// 
        /// 得到结点在结点表中的位置
        /// 
        /// 结点的名称
        /// 结点的位置
        private int GetIndex(string vertexName){
            int i;
            for (i = 0; i             {
                if (_vertexList[i] != null && _vertexList[i].VertexName == vertexName)
                    break;
            }
            return i == VertexCount ? -1 : i;
        }

        /// 
        /// 给图加边
        /// 
        /// 起始结点的名字
        /// 终止结点的名字
        /// 边上的权值
        public void AddEdge(string startVertexName, string endVertexName, double weight = 0.0){
            int i = GetIndex(startVertexName);
            int j = GetIndex(endVertexName);

            if (i == -1 || j == -1)
                throw new Exception("图中不存在该边.");

            EdgeNode temp = _vertexList[i].FirstNode;
            if (temp == null)
            {
                _vertexList[i].FirstNode = new EdgeNode(j, weight);
            }
            else
            {
                while (temp.Next != null)
                    temp = temp.Next;
                temp.Next = new EdgeNode(j, weight);
            }
        }
    }
}

3. 图的遍历

3.1 深度优先搜索

算法:

假设以为起点(源点)进行搜索。

首先标识为已访问结点,接着寻找与相邻的结点,若是未被访问结点,则以为起点进行深度优先搜索,若是已被访问结点,则寻找其它与相邻的结点,直到与有路径相通的所有结点都被访问过。

例子:

d436d0e4c10952706573c37d450531eb.png
图21 图的结构

深度优先搜索的序列为:

虽然遍历序列不唯一,但是邻接表确定后,遍历序列就唯一了。

实现:

dd8b1a3bd86d805a3eb343cf7377980d.png
图22 AdGraph结构
private void Dfs(int i, ref string dfsResult){
    //深度优先搜索递归函数
    _vertexList[i].Visited = true;
    dfsResult += _vertexList[i].VertexName + "\n";

    EdgeNode p = _vertexList[i].FirstNode;
    while (p != null)
    {
        if (_vertexList[p.Index].Visited)
            p = p.Next;
        else
            Dfs(p.Index, ref dfsResult);
    }
}

/// 
/// 得到深度优先搜索序列
/// 
/// 进行深度优先搜索的起始点名称
/// 深度优先搜索序列
public string DfsTraversal(string startVertexName){
    string dfsResult = string.Empty;
    int i = GetIndex(startVertexName);
    if (i != -1)
    {
        for (int j = 0; j             _vertexList[j].Visited = false;
        Dfs(i, ref dfsResult);
    }
    return dfsResult;
}

3.2 广度优先搜索

算法:

假设以为起点(源点)进行搜索。

首先标识为已访问结点,接着访问的邻接点然后访问的未被访问的邻接点,以此类推,直到与有路径相连的所有结点都被访问到。

先来先服务的思想。

例子:

1155f8188b513ba16ab3cb380f27c04b.png
图23 图的结构

深度优先搜索的序列为:

虽然遍历序列不唯一,但是邻接表确定后,遍历序列就唯一了。

实现:

2780c9fd9ceb78953b6daeb8080f3298.png
图24 AdGraph结构
/// 
/// 得到广度优先搜索序列
/// 
/// 进行广度优先搜索的起始点名称
/// 广度优先搜索序列
public string BfsTraversal(string startNodeName){
    string bfsResult = string.Empty;
    int i = GetIndex(startNodeName);
    if (i != -1)
    {
        for (int j = 0; j             _vertexList[j].Visited = false;

        _vertexList[i].Visited = true;
        bfsResult += _vertexList[i].VertexName + "\n";
        LinkQueue<int> lq = new LinkQueue<int>();
        lq.EnQueue(i);
        while (lq.IsEmpty() == false)
        {
            int j = lq.QueueFront;
            lq.DeQueue();
            EdgeNode p = _vertexList[j].FirstNode;
            while (p != null)
            {
                if (_vertexList[p.Index].Visited == false)
                {
                    _vertexList[p.Index].Visited = true;
                    bfsResult += _vertexList[p.Index].VertexName + "\n";
                    lq.EnQueue(p.Index);
                }
                p = p.Next;
            }
        }
    }
    return bfsResult;
}

4. 拓扑排序

4.1 基本概念

网(Activity on Vertex Network):用顶点表示活动,用有向边表示活动之间先后关系的有向图。

拓扑序列:把网中的所有顶点排成一个线性序列,使每个活动的所有前驱活动都排在该活动的前边。

拓扑排序:构造网拓扑序列的过程。

例题:按照拓扑排序安排下列课程

eafb42775c5807ea826318bedf866ee9.png
图25 课程表
0f516944f74b0794660cee061306fb93.png
图26 AOV网
3da81ea4b13a03c52fa7d6165afb1f74.png
图27 AOV网的存储

4.2 算法步骤

第1步:从网中选择一个入度为0的顶点加入排序序列。

第2步:从网中删除该顶点及其所有出边。

第3步:执行第1、2步,直到所有顶点已排序或网中剩余顶点入度均不为0(说明:网中存在回路,无法继续拓扑排序)。

拓扑序列:

(1)

(2)

注:对于任何无回路的网,其顶点均可排成拓扑序列,但其拓扑序列未必唯一。

62b7fb9adff35e5e91a8a9888572acc5.png
图28 存在回路的AOV网

存在回路的有向图,不能构成拓扑序列。

4.3 算法实现

3d635fb40255fba0dc35dcbfc2d3036e.png
图29 AdGraph结构
/// 
/// 得到每个节点的入度
/// 
/// 
private int[] GetInDegressList()
{
    int[] id = new int[VertexCount];
    for (int i = 0; i     {
        EdgeNode p = _vertexList[i].FirstNode;
        while (p != null)
        {
            id[p.Index]++;
            p = p.Next;
        }
    }
    return id;
}

/// 
/// 得到AOV网的拓扑排序序列
/// 
/// AOV网的拓扑排序序列
public string TopoSort(){
    string result = string.Empty;
    int[] id = GetInDegressList();
    LinkQueue<int> lq = new LinkQueue<int>();
    for (int i = 0; i     {
        if (id[i] == 0)
            lq.EnQueue(i);
    }

    if (lq.Length == VertexCount)
        throw new Exception("此有向图无有向边.");

    while (lq.IsEmpty() == false)
    {
        int j = lq.QueueFront;
        lq.DeQueue();
        result += _vertexList[j].VertexName + "\n";

        EdgeNode p = _vertexList[j].FirstNode;
        while (p != null)
        {
            id[p.Index]--;
            if (id[p.Index] == 0)
            {
                lq.EnQueue(p.Index);
            }
            p = p.Next;
        }
    }
    int k;
    for (k = 0; k     {
        if (id[k] != 0)
        {
            break;
        }
    }
    return (k == VertexCount) ? result : "该AOV网有环.";
}

5. 最小生成树

5.1 基本概念

生成树:设为连通网,具有的所有顶点(个)且只有条边的连通子网。

树的权:生成树的各边的权值总和。

最小生成树:权值最小的生成树。

5.2 Prim算法

算法原理(贪心算法):

设为连通网,为其对应的最小生成树,从开始构造。

(1)开始时,,。

(2)找到满足的边,把它并入中,同时并入。

(3)反复执行(2),直到时,终止算法。

例题:

99342c4f4adb4545a38357ea3a8d499a.png
图30 连通图
e2f89478944f2a317bc219f6bbff74c1.png
图31 连通图的存储
8abcf6a8dcf943295367526780f4ef63.png
图32 最小生成树的构建

算法实现:

最小生成树结点的存储:

bd1c9c596d582de7c6694f01160f0e50.png
图33 最小生成树结点的结构
940f43c3d83656bdb3382779eddfb82e.png
图34 SpanTreeNode结构
using System;

namespace NonLinearStruct
{
    /// 
    /// 生成树结点的抽象数据类型
    /// 
    public class SpanTreeNode
    {
        /// 
        /// 获取或设置结点本身的名称
        /// 
        public string SelfName { get; }

        /// 
        /// 获取或设置结点双亲的名称
        /// 
        public string ParentName { get; }

        /// 
        /// 获取或设置边的权值
        /// 
        public double Weight { get; set; }

        /// 
        /// 构造SpanTreeNode实例
        /// 
        /// 结点本身的名称
        /// 结点双亲的名称
        /// 边的权值
        public SpanTreeNode(string selfName, string parentName, double weight){
            if (string.IsNullOrEmpty(selfName) || string.IsNullOrEmpty(parentName))
                throw new ArgumentNullException();

            SelfName = selfName;
            ParentName = parentName;
            Weight = weight;
        }
    }
}
e8189656c5493eae2cd1fbff6118aae3.png
图35 AdGraph结构
/// 
/// 得到连通网的最小生成树Prim算法
/// 
/// 树根结点
/// 连通网的最小生成树
public SpanTreeNode[] MiniSpanTree(string vName)
{
    int i = GetIndex(vName);
    if (i == -1)
        return null;

    SpanTreeNode[] spanTree = new SpanTreeNode[VertexCount];
    spanTree[0] = new SpanTreeNode(_vertexList[i].VertexName, "NULL", 0.0);

    //U中结点到各结点最小权值那个结点在VertexList中的索引号
    int[] vertexIndex = new int[VertexCount];
    //U中结点到各个结点的最小权值
    double[] lowCost = new double[VertexCount];
    for (int j = 0; j     {
        lowCost[j] = double.MaxValue;
        vertexIndex[j] = i;
    }

    EdgeNode p1 = _vertexList[i].FirstNode;
    while (p1 != null)
    {
        lowCost[p1.Index] = p1.Weight;
        p1 = p1.Next;
    }
    vertexIndex[i] = -1;

    for (int count = 1; count     {
        double min = double.MaxValue;
        int v = i;
        for (int k = 0; k         {
            if (vertexIndex[k] != -1 && lowCost[k]             {
                min = lowCost[k];
                v = k;
            }
        }
        spanTree[count] = new SpanTreeNode(_vertexList[v].VertexName, _vertexList[vertexIndex[v]].VertexName, min);
        vertexIndex[v] = -1;

        EdgeNode p2 = _vertexList[v].FirstNode;
        while (p2 != null)
        {
            if (vertexIndex[p2.Index] != -1 && p2.Weight             {
                lowCost[p2.Index] = p2.Weight;
                vertexIndex[p2.Index] = v;
            }
            p2 = p2.Next;
        }
    }
    return spanTree;
}

5.3 Kruskar算法

设为连通网,为其对应的最小生成树。

(1)初始时,即中没有边,只有个顶点,就是个连通分量。

(2)在中选择权值最小的边,并将此边从中删除。

(3)如果,在的不同的连通分量中,则将加入到中,从而导致中减少一个连通分量。

(4)反复执行(2)(3)直到中仅剩一个连通分量时,终止操作。

例题:

d0891ba3cf8df8642a57f6d991059eb0.png
图36 最小生成树的构建
981fb174529272ac8219bed273d45c71.png
图37 连通图的存储
dbe6304cfc3e60d5a929414950b68c02.png
图38 边的集合

算法实现:

759c6830bab41fef01739cdd35455393.png
图39 Edge结构
namespace NonLinearStruct.Graph
{
    /// 
    /// 表示图的边
    /// 
    public class Edge
    {
        /// 
        /// 起点编号
        /// 
        public int Begin { get; }

        /// 
        /// 终点编号
        /// 
        public int End { get; }

        /// 
        /// 权值
        /// 
        public double Weight { get; }

        /// 
        /// 创建一个 Edge 类的新实例
        /// 
        /// 起点编号
        /// 终点编号
        /// 权值
        public Edge(int begin, int end, double weight = 0.0){
            Begin = begin;
            End = end;
            Weight = weight;
        }
    }
}
ac8a09fc15afa87f5ffbe403b8982190.png
图40 AdGraph结构
private Edge[] GetEdges()
{
    for (int i = 0; i         _vertexList[i].Visited = false;

    List result = new List();for (int i = 0; i     {
        _vertexList[i].Visited = true;
        EdgeNode p = _vertexList[i].FirstNode;while (p != null)
        {if (_vertexList[p.Index].Visited == false)
            {
                Edge edge = new Edge(i, p.Index, p.Weight);
                result.Add(edge);
            }
            p = p.Next;
        }
    }return result.OrderBy(a => a.Weight).ToArray();
}private int Find(int[] parent, int f){while (parent[f] > -1)
        f = parent[f];return f;
}/// /// 克鲁斯卡尔算法 最小生成树/// /// public SpanTreeNode[] MiniSpanTree()
{int[] parent = new int[VertexCount];for (int i = 0; i     {
        parent[i] = -1;
    }
    SpanTreeNode[] tree = new SpanTreeNode[VertexCount];
    Edge[] edges = GetEdges();int count = 1;for (int i = 0; i     {int begin = edges[i].Begin;int end = edges[i].End;int b = Find(parent, begin);int e = Find(parent, end);if (b != e)
        {
            parent[e] = b;
            tree[count] = new SpanTreeNode(_vertexList[end].VertexName,
                _vertexList[begin].VertexName, edges[i].Weight);
            count++;
        }
    }for (int i = 0; i     {if (parent[i] == -1)
        {
            tree[0] = new SpanTreeNode(_vertexList[i].VertexName, "NULL", 0.0);break;
        }
    }return tree;
}

6. 单源最短路径

6.1 定义

设为连通网,为源点,到其余各顶点的最短路径问题即为单源最短路径问题。

6.2 迪杰特撕拉算法

(迪杰特斯拉)算法(按路径长度递增顺序构造的算法):

初始时为源点,, ,标识被访问。

(1)令,(到的路径长度)。

(2)依次考察的未被访问的邻接点,若,则改变的值,使。

(3)在未被访问顶点中选择最小的顶点,访问。

(4)重复(2)、(3)直至所有顶点都被访问。

例题:

5125a6910d8249cfe0595048d1287d9e.png
图41 原始图
c7af1ead7364517734c2777ccd768f3f.png
图42 图的存储结构
4620f07e3c0034d5e6635419072c5411.png
图43 最短路径结果

算法实现:

387313757174913b60534c3c6379bee1.png
图44 AdGraph结构
/// 
/// 单源最短路径
/// 
/// 寻找最短路径的源点
/// 源点到各个顶点的最短路径
public string ShortestPath(string vName){
    int v = GetIndex(vName);
    if (v == -1)
    {
        return string.Empty;
    }

    string result = string.Empty;
    double[] dist = new double[VertexCount];
    string[] path = new string[VertexCount];
    //初始化
    for (int i = 0; i     {
        _vertexList[i].Visited = false;
        dist[i] = double.MaxValue;
        path[i] = _vertexList[v].VertexName;
    }
    dist[v] = 0.0;
    _vertexList[v].Visited = true;

    for (int i = 0; i 1; i++)
    {
        EdgeNode p = _vertexList[v].FirstNode;
        while (p != null)
        {
            if (_vertexList[p.Index].Visited == false
                && dist[v] + p.Weight             {
                dist[p.Index] = dist[v] + p.Weight;
                path[p.Index] = path[v] + " ->" + _vertexList[p.Index].VertexName;
            }
            p = p.Next;
        }

        double min = double.MaxValue;
        for (int j = 0; j         {
            if (_vertexList[j].Visited == false && dist[j]             {
                min = dist[j];
                v = j;
            }
        }
        _vertexList[v].Visited = true;
    }

    for (int i = 0; i     {
        result += path[i] + ":" + dist[i] + "\n";
    }

    return result;
}

7. 连通分量

例题:

2c9be451536ed02a89fe817b68438aae.png
图45 原始图
96207b3c3fc5e8946c63a0f49ca9a86d.png
图46 图的存储结构

可利用深度优先搜索,求非连通图的连通分量。

第一个:A,B,D,C;第二个:E,F;第三个:G,H,I

算法实现:

5a4824108f5075fb1b6040dfaa11c898.png
图47 AdGraph结构
/// 
/// 得到连通分量
/// 
/// 连通分量
public string ConnectedComponent(){
    string result;
    SLinkList<string> lst = new SLinkList<string>();

    for (int i = 0; i         _vertexList[i].Visited = false;

    for (int i = 0; i     {
        if (_vertexList[i].Visited == false)
        {
            result = string.Empty;
            //利用深度优先搜索求非连通图的连通分量
            Dfs(i, ref result);
            lst.InsertAtRear(result);
        }
    }
    result = string.Empty;
    for (int i = 0; i     {
        result += "第" + i + "个连通分量为:\n" + lst[i];
    }
    return result;
}

8. 练习

根据要求完成程序代码:

给定纽约市附近的一幅简单地图,图中的顶点为城市,无向边代表两个城市的连通关系,边上的权为两城市之间的距离。

96ad3b2865a3f79d474e16c0d423bdaa.png
图48 纽约市附近地图

(1)对该图进行深度优先和广度优先搜索,并输出搜索序列(图的搜索问题)。

(2)在分析这张图后可以发现,任一对城市都是连通的。

第一个问题是:要用公路把所有城市连接起来,如何设计可使得工程的总造价最少(最小生成树问题)。

第二个问题是:要开车从一个城市到另外一个城市求其最短距离以及驱车路线(最短路径问题)。

解答:

数据TXT文件:

Source,Target,Weight
San Rapheal,Cross,12
San Rapheal,Oakland,18
Cross,Daly Cit,3
Cross,San Franciso,3
Daly Cit,San Franciso,4
Daly Cit,Cross B,19
San Franciso,Oakland,7
San Franciso,San Mateo,21
Oakland,San Iarenzo,18
Oakland,Dublin,31
San Iarenzo,Hayward,3
San Iarenzo,Dublin,12
Cross B,San Mateo,4
Cross B,Cross C,7
San Mateo,Hayward,13
San Mateo,Redwood City,6
Hayward,Freemont,9
Dublin,San Jose,35
Redwood City,Cross C,5
Redwood City,Palo Alto,6
Cross C,Cupertin,14
Palo Alto,Freemont,9
Palo Alto,Mtn View,6
Freemont,San Jose,24
Mtn View,Cupertin,6
Mtn View,San Jose,8
Cupertin,San Jose,7

深度优先搜索序列:

San Rapheal
Cross
Daly Cit
San Franciso
Oakland
San Iarenzo
Hayward
San Mateo
Cross B
Cross C
Redwood City
Palo Alto
Freemont
San Jose
Dublin
Mtn View
Cupertin

广度优先搜索序列:

San Rapheal
Cross
Oakland
Daly Cit
San Franciso
San Iarenzo
Dublin
Cross B
San Mateo
Hayward
San Jose
Cross C
Redwood City
Freemont
Mtn View
Cupertin
Palo Alto

最小生成树:

(NULL,San Rapheal) Weight:0
(San Rapheal,Cross) Weight:12
(Cross,Daly Cit) Weight:3
(Cross,San Franciso) Weight:3
(San Franciso,Oakland) Weight:7
(Oakland,San Iarenzo) Weight:18
(San Iarenzo,Hayward) Weight:3
(Hayward,Freemont) Weight:9
(Freemont,Palo Alto) Weight:9
(Palo Alto,Redwood City) Weight:6
(Redwood City,Cross C) Weight:5
(Redwood City,San Mateo) Weight:6
(San Mateo,Cross B) Weight:4
(Palo Alto,Mtn View) Weight:6
(Mtn View,Cupertin) Weight:6
(Cupertin,San Jose) Weight:7
(San Iarenzo,Dublin) Weight:12

最小生成树权值:116

最短路径:

San Rapheal:0
San Rapheal ->Cross:12
San Rapheal ->Oakland:18
San Rapheal ->Cross ->Daly Cit:15
San Rapheal ->Cross ->San Franciso:15
San Rapheal ->Cross ->Daly Cit ->Cross B:34
San Rapheal ->Cross ->San Franciso ->San Mateo:36
San Rapheal ->Oakland ->San Iarenzo:36
San Rapheal ->Oakland ->San Iarenzo ->Dublin:48
San Rapheal ->Oakland ->San Iarenzo ->Hayward:39
San Rapheal ->Cross ->Daly Cit ->Cross B ->Cross C:41
San Rapheal ->Cross ->San Franciso ->San Mateo ->Redwood City:42
San Rapheal ->Oakland ->San Iarenzo ->Hayward ->Freemont:48
San Rapheal ->Cross ->San Franciso ->San Mateo ->Redwood City ->Palo Alto ->Mtn View ->San Jose:62
San Rapheal ->Cross ->San Franciso ->San Mateo ->Redwood City ->Palo Alto:48
San Rapheal ->Cross ->Daly Cit ->Cross B ->Cross C ->Cupertin:55
San Rapheal ->Cross ->San Franciso ->San Mateo ->Redwood City ->Palo Alto ->Mtn View:54

6244599eb5ebb12efcaddf4dfaa1df7b.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值