对于有 nn 个顶点的图 G = (V, E)来说,我们可以用一个 n * n的矩阵 AA来表示 G中各顶点的相邻关系,如果 v_{i}和 v_{j}之间存在边(或弧),则
A[i][j] = 1,
否则 A[i][j] = 0。
下图为有向图 G_{1}G1
以及对应的邻接矩阵。
下图为无向图 G_{2}G2
以及对应的邻接矩阵。
图的邻接矩阵是唯一的,矩阵的大小只与顶点个数 NN 有关,是一个 N * NN∗N 的矩阵。前面我们已经介绍过,在无向图里,如果顶点 v_{i}vi
v_{j}vj之间有边,则可认为顶点 v_{i}vi到 v_{j}vj有边,同时顶点 v_{j}vj到 v_{i}vi 也有边。对应到邻接矩阵里,
则有
A[i][j]=A[j][i]=1。
因此我们可以发现,无向图的邻接矩阵是一个对称矩阵。
在邻接矩阵上,我们可以直观的看出两个顶点之间是否有边(或弧),并能容易求出每个顶点的度,入度和出度。
这里我们以图 G_{1}G1为例,演示下如何利用邻接矩阵计算顶点的入度和出度。顶点的出度,即为邻接矩阵上点对应行上所有值的总和,比如顶点 1 对应的出度即为
0+1+1+1=3;而每个点的入度即为点对应列上所有值的总和,比如顶点 3 对应的入度即为
1+0+0+1=2。
我们再来学习邻接表
邻接表是图的一种顺序存储与链式存储相结合的存储方法。我们给图 GG 中的每个顶点建立一个单链表,第 ii 个单链表中的结点表示依附于顶点 v_{i} 的边(对于有向图是以 v_{i}为起点的弧)。所有单链表的表头结点都存储在一个一维数组中,以便于顶点的访问。下图为图 G_{1}对应的邻接表。
在无向图的邻接表中,顶点 v_{i}的度为第 i个单链表中的结点数;而在有向图中,第 ii 个单链表中的结点数表示的是顶点 v_{i}v
i的出度,如果要求入度,则要遍历整个邻接表。另外,在邻接表中,我们很容易就能知道某一顶点和哪些顶点相连接。
学习完两种存储结构,可能你会有这样的疑问:那我们什么时候用邻接矩阵,什么时候用邻接表呢?
我们可以看到,邻接矩阵存储结构最大的优点就是简单直观,易于理解和实现。其适用范围广泛,有向图、无向图、混合图、带权图等都可以直接用邻接矩阵表示。另外,对于很多操作,比如获取顶点度数,判断某两点之间是否有连边等,都可以在常数时间内完成。
然而,它的缺点也是显而易见的:从以上的例子我们可以看出,对于一个有 n 个顶点的图,邻接矩阵总是需要n2的存储空间。当边数很少的时候,就会造成空间的浪费。
因此,具体使用哪一种存储方式,要根据图的特点来决定:如果是稀疏图,我们一般用邻接表来存储,这样可以节省空间;如果是稠密图,考虑到邻接表中要附加链域,我们一般用邻接矩阵来存储
邻接表的创建
#include <iostream>
#include <cstring>
using namespace std;
class Graph {
private:
int **mat;
int n;
public:
Graph(int input_n) {
n = input_n;
mat = new int*[n];
for (int i = 0; i < n; ++i) {
mat[i] = new int[n];
memset(mat[i], 0, sizeof(int) * n);
}
}
~Graph() {
for (int i = 0; i< n; ++i) {
delete[] mat[i];
}
delete[] mat;
}
void insert(int x, int y) {
mat[x][y]=1;
}
void output() {
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cout<<mat[i][j]<<" ";
}
cout<<endl;
}
}
};
int main() {
int n, m, x, y;
cin >> n >> m;
Graph g(n);
for (int i = 0; i < m; ++i) {
cin >> x >> y;
g.insert(x, y);
}
g.output();
return 0;
}
邻接表
#include <iostream>
using namespace std;
class LinkedListNode {
public:
int vertex;
LinkedListNode *next;
LinkedListNode(int vertex_input) {
vertex = vertex_input;
next = NULL;
}
};
class LinkedList {
public:
LinkedListNode *head;
LinkedList() {
head = NULL;
}
~LinkedList() {
while (head != NULL) {
LinkedListNode *delete_node = head;
head = head->next;
delete delete_node;
}
}
void insert(int vertex) {
LinkedListNode *node = new LinkedListNode(vertex);
node->next = head;
head = node;
}
};
class Graph {
private:
LinkedList *edges;
int n;
public:
Graph(int input_n) {
n = input_n;
edges = new LinkedList[n];
}
~Graph() {
delete[] edges;
}
void insert(int x, int y) {
edges[x].insert(y);
}
void output() {
for(int i=0;i<n;i++){
cout<<i<<":";
for(auto j=edges[i].head;j!=NULL;j=j->next){
cout<<j->vertex<<" ";
}
cout<<endl;
}
}
};
int main() {
int n, m, x, y;
cin >> n >> m;
Graph g(n);
for (int i = 0; i < m; ++i) {
cin >> x >> y;
g.insert(x, y);
}
g.output();
return 0;
}