图 Graph

Graph

​ 在线性表中,数据结构是被串起来的,仅有线性关系,每个数据结构只有一个前驱和一个后继,而在树形结构中数据元素之间有明显的层次关系,并且每一层上的数据元素可能和下一层中的多个元素相关,但只和上一层中的一个元素相关。
​ 在许多计算机应用中情况可能会变得更加复杂,由相连的节点所表示的模型起到了关键作用。这些节点之间有多种关系产生了多种问题:这些连接是否能从一个节点到达另一个节点,有多少个节点和指定的节点连接,两个节点之间最短的连接是哪一条。由此诞生了数学领域中重要的一个分支————图论。
​ 图论的应用相当广泛:地图上的两个点之间的最短路线、公路与公路之间的节点;网页上每一个链接都代表着不同的网页,从一个页面跳到另一个页面,整个互联网就相当于一张巨大的图;电路板上各个元器件、晶体管、电阻、电容紧密的连接在一起,涉及的导线元件特性的问题。

图的分类

​ 图分为无向图,有向图、加权图和加权有向图。我们首先要学习无相图。在这种图的模型中,边仅仅是两个顶点之间的连接。表示为

\[G(V,E) \]

其中 \(G\) 表示一个图,\(V\) 是图中所有顶点(vertex)的集合,\(E\) 是图中所有边(edge)的集合

与图相关的术语

  • 无向图中顶点v的度(\(degree(v)\))是和v相连的边的数目;有向图中以v为弧尾的弧的数目为出度(\(out\space degree\)),为弧头的弧的数目为入度(\(in\space degree\))
  • 路径是由边顺序连接的一系列顶点
  • 简单路径是一条没有重复顶点的路径
  • 是至少含有一条边,且起点和终点相同的路径
  • 简单环是一条,除了起点和终点必须相同之外,不含有重复顶点和边的环
  • 路径或环的长度为其中包含的边数
  • 连通图从任意顶点都存在一条路径到达另一任意顶点
  • 是一幅无环连通图,互不相连的的组成的集合称为森林
  • 连通度是指已经连接的顶点对占所有可能被连接的顶点对的比例,在稀疏图中被连接的顶点对很少,而在稠密图中,只有少部分顶点对之间没有边连接
  • 如果顶点 \(v_i\)\(v_j\) 之间的边没有方向,则称这条边为无向边,用无序偶对 \((\large v_i,\large v_j)\) 来表示
  • 如果从顶点 \(v_i\)\(v_j\) 之间的边有方向,则称这条边为有向边,也称为弧。用有序偶对 \(\small{<}\large v_i,\large v_j\small{>}\) 表示,其中 \(v_i\) 称为弧尾,\(v_j\) 称为弧头
  • 如果不存在顶点到其自身的边,且同一条边不重复出现,这样的图为简单图
  • 无向图中,如果任意两个顶点之间都存在边,造成该图为无向完全图;有向图中,如果任意两个顶点之间都存在方向互为相反的两条弧组成,该图为有向完全图
  • 现有两个图 \(G=(V,E)\)\(G'=(V',E')\),若 \(V'\subseteq V\)\(E'\subseteq E\) 则称 \(G'\)\(G\)子图,无向图中的极大连通子图称为连通分量

图的表示方法

  1. 邻接矩阵
    用一个 \(V \times V\) 的布尔矩阵(整型矩阵)当顶点 v 和顶点 w 有连接时,定义 v 行 w 列的值为 True
  2. 边集数组
    用一个类 Edge,它含有两个顶点变量
  3. 邻接表数组
    使用一个以顶点为索引的数组,其中的每个元素都与该顶点相连
  4. 十字链表
  5. 邻接集
结构 占用空间 添加一条边 检查v和w之间是否连接 遍历v的相连顶点
邻接矩阵 \(V^2\) \(1\) \(1\) \(V\)
边集数组 \(E\) \(1\) \(E\) \(E\)
邻接表数组 \(E+V\) \(1\) \(degree(v)\) \(degree(v)\)

以邻接表数组表示的图

using GraphNode = List<int>;
public class Graph
{
    public int V { get; }
    public int E { get; private set; }
    private GraphNode[] _Vertexes;

    public Graph(int v)
    {
        V = v;
        E = 0;
        _Vertexes = new GraphNode[V];
        for (int i = 0; i != V; ++i)
            _Vertexes[i] = new GraphNode();
    }
    public Graph(int v, string edges) : this(v)
    {
        var links = from edge in edges.Split(',')
            where edge != ""
            let vw = edge.Trim().Split(' ')
            select (int.Parse(vw[0]), int.Parse(vw[1]));
        foreach ((int v, int w) link in links)
            Link(link.v, link.w);
    }

    public void Link(int v, int w)
    {
        if (v >= V || w >= V || v < 0 || w < 0)
            throw new ArgumentOutOfRangeException();
        _Vertexes[v].Add(w);
        _Vertexes[w].Add(v);
        ++E;
    }
    public bool IsLinked(int v, int w) { return _Vertexes[v].Contains(w); }
    public int Degree(int v) { return _Vertexes[v].Count; }
    public IEnumerable<int> Adj(int v) { return _Vertexes[v]; }

    public override string ToString()
    {
        var s = new System.Text.StringBuilder(V + " vertices, " + E + " edges" + NewLine);
        for (int v = 0; v != V; ++v)
        {
            s.Append("\t" + v + ": ");
            foreach (int w in _Vertexes[v])
                s.Append(w + " ");
            s.Append(NewLine);
        }
        return s.ToString();
    }
}

图的搜索

分为深度优先搜索(DepthFirstSearch)广度优先搜索(BreadthFirstSearch)

  • 深度优先搜索:

    在访问一个顶点时:

    • 将它标记为已访问
    • 递归地访问它没有被标记的相连节点

    可以回答连通性问题

  • 广度优先搜索:

    先将起点加入队列:

    • 取出队节点并标记它
    • 将与它相连且未标记的节点加入队列

    可以回答最短路径问题

public IEnumerable<int> DeepFirstSearch(int v)
{
    if (v >= V || v < 0)
        throw new ArgumentOutOfRangeException();
    bool[] marked = new bool[V];
    Stack<int> path = new Stack<int>();
    do
    {
        if (marked[v]) continue;
        yield return v;
        marked[v] = true;
        foreach (int w in _Vertexes[v])
            if (!marked[w])
                path.Push(w);
    } while (path.TryPop(out v));
}
public IEnumerable<int> BreadthFirstSearch(int v)
{
    if (v >= V || v < 0)
        throw new ArgumentOutOfRangeException();
    bool[] marked = new bool[V];
    Queue<int> path = new Queue<int>();
    do
    {
        if (marked[v]) continue;
        yield return v;
        marked[v] = true;
        foreach (int w in _Vertexes[v])
            if (!marked[w])
                path.Enqueue(w);
    } while (path.TryDequeue(out v));
}
posted @ 2022-10-28 11:25  Violeshnv  阅读(33)  评论(0编辑  收藏  举报