图论-图的存储方式

图的存储方式:

1,数组表示法:

用两个数组来存储图的信息

  • 顶点表:记录各个顶点信息的
  • 邻接矩阵:表示各个顶点之间的关系(有关为1,无关为0)

注:无向图的邻接矩阵是对称的,有向图的邻接矩阵可能是不对称的。

无向图的邻接矩阵

结点i的度=邻接矩阵中第i行或第i列之和

存储压缩:上三角矩阵或下三角矩阵(不含对角线)

有向图的邻接矩阵

结点i的出度=邻接矩阵中第i行之和

结点i的入度=邻接矩阵中第i列之和

A.edge[i][j]= 如果两点直接存在边且有权值,就记为权值,自环或者无边就记为∞

优点:容易实现图的各种基本操作

{

查找顶点,

判断顶点之间是否有边,仅仅耗费O(1)时间

找顶点的邻接点

}

缺点:稀疏图,即使边数<<n^2,也需要占用O(n^2)的存储单元,读入数据需要耗费O(n^2)时间

邻接矩阵表示方法,code:

 1 #include <iostream>
 2 #include <vector>
 3 
 4 using namespace std;
 5 
 6 int n, m;
 7 vector<bool> vis;
 8 vector<vector<bool> > adj;
 9 
10 bool find_edge(int u, int v) { return adj[u][v]; }
11 
12 void dfs(int u) {
13   if (vis[u]) return;
14   vis[u] = true;
15   for (int v = 1; v <= n; ++v) {
16     if (adj[u][v]) {
17       dfs(v);
18     }
19   }
20 }
21 
22 int main() {
23   cin >> n >> m;
24 
25   vis.resize(n + 1, false);
26   adj.resize(n + 1, vector<bool>(n + 1, false));
27 
28   for (int i = 1; i <= m; ++i) {
29     int u, v;
30     cin >> u >> v;
31     adj[u][v] = true;
32   }
33 
34   return 0;
35 }

 

2,邻接表表示法

图的一种链式存储结构

顶点表:记录各个顶点的信息,通常以顺序结构存储,也可以链式存储(vector存储)

邻接链表:为每个顶点建立一个单链表,表示依附于改顶点的所有边(对有向图是以该节点为尾的所有弧)

无向图的邻接表

优点:n个顶点,e条边的无向图,只需要n个头结点和2e个表节点。对稀疏图而言,比数组表示法节省存储空间。

缺点:每条边需要对应两个表结点

有向图的邻接表

缺点:

{

查找进入某个结点的弧不易

求某个顶点的入度需要遍历整个邻接表

}

改进:建立逆邻接表

邻接表表示法code:

#include <iostream>
#include <vector>

using namespace std;

int n, m;
vector<bool> vis;
vector<int> adj[n+1];

bool find_edge(int u, int v) {
  for (int i = 0; i < adj[u].size(); ++i) {
    if (adj[u][i] == v) {
      return true;
    }
  }
  return false;
}

void dfs(int u) {
  if (vis[u]) return;
  vis[u] = true;
  for (int i = 0; i < adj[u].size(); ++i) dfs(adj[u][i]);
}

int main() {
  cin >> n >> m;

  vis.resize(n + 1, false);
  adj.resize(n + 1);

  for (int i = 1; i <= m; ++i) {
    int u, v;
    cin >> u >> v;
    adj[u].push_back(v);
  }

  return 0;
}

3,链式前向星:

本质上是用链表实现的邻接表,核心代码如下:

// head[u] 和 cnt 的初始值都为 -1
void add(int u, int v) {
  next[++cnt] = head[u];  // 当前边的后继
  head[u] = cnt;         // 起点 u 的第一条边
  to[cnt] = v;           // 当前边的终点
}

// 遍历 u 的出边
for (int i = head[u]; ~i; i = next[i]) {  // ~i 表示 i != -1
  int v = to[i];
}

代码:

#include <iostream>
#include <vector>

using namespace std;

int n, m;
vector<bool> vis;
vector<int> head, next, to;

void add(int u, int v) {
  next.push_back(head[u]);
  head[u] = to.size();
  to.push_back(v);
}

bool find_edge(int u, int v) {
  for (int i = head[u]; ~i; i = nxt[i]) {  // ~i 表示 i != -1
    if (to[i] == v) {
      return true;
    }
  }
  return false;
}

void dfs(int u) {
  if (vis[u]) return;
  vis[u] = true;
  for (int i = head[u]; ~i; i = next[i]) dfs(to[i]);
}

int main() {
  cin >> n >> m;

  vis.resize(n + 1, false);
  head.resize(n + 1, -1);

  for (int i = 1; i <= m; ++i) {
    int u, v;
    cin >> u >> v;
    add(u, v);
  }

  return 0;
}

4,十字链表

5,邻接多重表表示

6,直接存边:

 

#include <iostream>
#include <vector>

using namespace std;

struct Edge {
  int u, v;
};

int n, m;
vector<Edge> e;
vector<bool> vis;

bool find_edge(int u, int v) {
  for (int i = 1; i <= m; ++i) {
    if (e[i].u == u && e[i].v == v) {
      return true;
    }
  }
  return false;
}

void dfs(int u) {
  if (vis[u]) return;
  vis[u] = true;
  for (int i = 1; i <= m; ++i) {
    if (e[i].u == u) {
      dfs(e[i].v);
    }
  }
}

int main() {
  cin >> n >> m;

  vis.resize(n + 1, false);
  e.resize(m + 1);

  for (int i = 1; i <= m; ++i) cin >> e[i].u >> e[i].v;

  return 0;
}

 

上一篇:欧拉回路


下一篇:Codeforces 1152E(欧拉路径)