图是一个多对多关系的集合,是非线性数据结构。
图由顶点(Vertex)的集合和边(Edge)的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
一、图的基本概念
1.有向图和无向图
无向图:每条边都没有方向。
有向图:每条边都有方向。
2.顶点的度
对于无向图,顶点的度表示以该顶点作为一个端点的边的数目。
对于有向图,顶点分为入度和出度。入度表示以该顶点为终点的入边数目,出度是以该顶点为起点的出边数目,该顶点的度等于其入度和出度之和。
3.完全图
无向完全图:具有n(n-1)/2条边的无向图
有向完全图:具有n(n-1)条边的有向图。
稠密图:一个边数接近完全图的图。
稀疏图:一个边数远远少于完全图的图。
4.连通图
连通图:图G中任意两个顶点U,V之间存在一条从U通过若干边,点到达V的通路,则G是连通的。
强连通分量:有向图中任意两个点都连通的最大子图。
下图的强连通分量:1-2-3,4,5
二、图的邻接矩阵存储
邻接矩阵存储,就是用矩阵表示图中各顶点之间的邻接关系和权值。通常采用二维数组存储。
如下图:
M条边,N个顶点:
#include<iostream>
using namespace std;
// const int N = 5;
// const int M = 4;
const int oo = 0x7f;
int G[101][101];
int main(){
//邻接矩阵存储
int N,M;
cin >> N >> M;
for(int i = 1; i <= N; i++){
for(int j = 1; j <= N; j++){
G[i][j] = oo; //初始为无穷大;
}
}
for(int k = 1; k <= M; k++){
int x,y,w;
cin >> x >> y >> w;//读入顶点和边权
G[x][y] = w; //存储从点x到点y的边权
G[y][x] = w;//无向图需要
}
//邻接矩阵访问
for(int i = 1; i <= N; i++){
for(int j = 1; j <= N; j++){
if(G[i][j] < oo){
cout << i <<" "<< j <<" "<< G[i][j] << endl;
}
}
}
}
三、图的邻接表存储❤️❤️❤️(常用)
邻接表存储又叫链式存储。通常不用链表实现,用数组模拟即可。
一个有向图有N个顶点,M条边。
编号计数器num:出边的编号计数器,1≤num≤M
表头数组head[i]:存储顶点i的当前出边的编号num,1<=i<=N;
终点数组to[num]:存储num号边的终点,即顶点i的邻接点。
边权数组W[num]:存储num号边的权值。
指针数组:next[num]:存储num号边的下一条边。
当顶点很多时,邻接矩阵寻找某个顶点的邻接点效率较低,若为顶点很多的稀疏图,邻接表比较好。
代码实现:
❤️❤️❤️
与单链表的头插法相似
#include<bits/stdc++.h>
using namespace std;
int num = 0;
int head[21]; //表头数组
int to[51]; //当前边的终点
int W[51]; //当前边的权值
int next[51]; //下一条边的编号
// struct info{
// int next;
// int to;
// int w;
// };
// info f[66];
void AddEdge(int v1,int v2,int w){
++num;
next[num] = head[v1];
to[num] = v2;
W[num] = w;
head[v1] = num;
}
int main(){
int N,M;
cin >> N >> M;
for(int i = 0; i < M; i++){
int x,y,w;
cin >> x >> y >> w;
AddEdge(x,y,w);
}
for(int i = head[1];i != 0;i = next[i]){
cout << 1 <<" " << to[i] <<" "<< W[i] << endl;
}
}
可以将to[],next[],W[]修改成结构体
用结构体邻接表
#include <bits/stdc++.h>
using namespace std;
int num = 0;
int head[21]; //表头数组
int to[51]; //当前边的终点
int w[51]; //当前边的权值
int next[51]; //下一条边的编号
struct info{
int next;
int to;
int w;
};
info f[66];
void AddEdge(int v1, int v2, int w)
{
++num;
f[num].next = head[v1];
f[num].to = v2;
f[num].w = w;
head[v1] = num;
}
int main()
{
int N, M;
cin >> N >> M;
for (int i = 1; i <= M; i++)
{
int x, y, w;
cin >> x >> y >> w;
AddEdge(x, y, w);
}
for (int i = head[1]; i != 0; i = f[i].next)
{
cout << 1 << " " << f[i].to << " " << f[i].w << endl;
}
}
四、图的邻接点存储
一个有向图有N个顶点,M条边。
计数数组num[i]:存储顶点i的邻接点个数;
邻接点数组A[i][k]:存储顶点i的邻接点;
权值数组W[i][A[i][k]]: 存储点i到点A[i][k]之间的边权。
#include <bits/stdc++.h>
using namespace std;
int num[61]; // 存储邻接点个数
int A[61][61]; //存储邻接点
int W[61][61]; //存储边权
int main()
{
int i, j, x, y, w;
int N, M;
cin >> N >> M;
for (int i = 1; i <= M; i++)
{
cin >> x >> y >> w;
A[x][++num[x]] = y;
W[x][y] = w;
//A[y][++num[y]] = x; 无向图需要
//W[y][x] = w;
}
// 邻接点访问
for (int i = 1; i <= N; i++)
for (int j = 1; j <= num[i]; j++)
// cout << i << A[i][j] << W[i][A[i][j]] ;
cout << i << " " << A[i][j] << " " << W[i][A[i][j]] << endl;
}
/*
4 6
1 2 10
1 4 11
2 3 8
2 4 12
1 3 7
3 4 9
*/