图的基本应用

本章内容难度大,为通俗易懂,结合洛谷讲述。

图的基本应用_第1张图片
  1. 图的概念和建立

下图,数字代表建筑物,黑色的线是道路。把每个建筑物称作顶点,每条直接连通的道路称作。一个图就是由这些顶点和边构成的集合。顶点连边的数量就是顶点的度数。两个顶点中间有不止一条边,这被称为重边。有时也会有自己到自己的边,被称为自环。而下图标注了走过这段道路所需时间,这个针对每一条边的属性 值被称为边权。类似地,顶点也可以有点权

图的基本应用_第2张图片

对于右下图,每条道路只能单向通行,即边是单向的,被称为有 向图。而左下图则是无向图,可以双向通行。 对于有向图: • 一个顶点向别的顶点连边的条数称作这个结点的出度。

• 别的顶点连边到一个顶点的条数称作这个结点的入度

图的基本应用_第3张图片

可以使用邻接矩阵存储一张图:记 v[i][j] 表示从 i 到 j 的边权。如果不通可以设置为 0 或者inf (一个很大的数字)。

无向图的邻接矩阵是对称的,有向图的邻接矩阵则不对称。

对于一个 n 个点 m 条边的图,在使用邻接矩阵时,需要开一个 n*n 的数组,即空间复杂度 O() ,代码如下:

cin >> n;
for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++)
        cin >> v[i][j]; // 存入每一对点之间的边权
for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++)
        if (v[i][j] > 0)
            cout << "edge from point " << i << " to point " << j<< " with length " << v[i][j] << '\n';

可采用 vector 代替二维数组,使用 vector 存储第二维,从而减少 空间占用。这种做法被称为邻接表。 为了同时存储边的终点与边权,可以采用结构体。

#include 
#include 
#define MAXN 1005
using namespace std;
struct edge {
    // 记录边的终点,边权的结构体
    int to, cost;
};
int n, m; // 图有 n 个点 m 条边
vector  p[MAXN]; // 邻接表
int v[MAXN][MAXN]; // 邻接矩阵
int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v, l;
        cin >> u >> v >> l;
        p[u].push_back((edge) {v, l});
    // p[v].push_back((edge){u, l});
    // 无向图邻接表要加一条反方向的边
    
    // 遍历邻接表,把邻接表转换为邻接矩阵
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < p[i].size(); j++)
            v[i][p[i][j].to] = p[i][j].cost;
    return 0;
}}

邻接表优点:总的空间复杂度是 O(m) 的,且遍历某个点相邻的 节点的时间复杂度为 O(p) ,其中 p 为该点的出度。 邻接表缺点:如果需要指定查询或修改边的边权,需要的时 间复杂度为 O(p),不如邻接矩阵的 O(1)。

邻接矩阵: 适用于点较少(几百)、边较多(稠密图)的情况。 邻接表: 适用于点较多、或者可能出现重边的情况。

  1. 图的遍历

深度优先遍历:特点是优先阅读最先被发现的文章,指导找不到就退回去,需要 使用栈维护阅读的层级(一般用递归来实现)。代码如下,方式如图:

void solve(int x) {
    for (int i = 0; i < p[x].size(); i++)
    if (!u[p[x][i]]) {
        u[p[x][i]] = true;
        solve(p[x][i]);
}}
图的基本应用_第4张图片

广度优先遍历:特点是优先阅读离初始论文距离更近的文章。需要使用队列来维 护等待阅读的文章。代码如下,方式如图:

while (!q.empty()) {
    int x = q.front();
    q.pop();
    cout << x << ' ';
    for (int i = 0; i < p[x].size(); i++)
        if (!u[p[x][i]]) {
            u[p[x][i]] = true;
            q.push(p[x][i]);
}}
图的基本应用_第5张图片
  1. DAG 与拓扑排序

对于一张有向图,如果它没有环,则称为有向无环图,简称 DAG。下面的图就是一个 DAG 的例子。

图的基本应用_第6张图片

对于 DAG,在有的时候,对于一个节点的信息的计算,需要确保 与之相连的点的信息全部被计算。这时就需要用到拓扑排序本质是确保 DAG 上的点的计算顺序而非对数列排序。

图的基本应用_第7张图片

例题:P5318 查找文献 P3916 图的遍历 P1113 杂务 P4017 最大食物链计数

你可能感兴趣的:(c++,数据结构)