文章目录





1 概念

1.1 定义

  • 由顶点集V(Vertex)和边集E(Edge)组成,其中V为有限非空集合,E表示图中顶点之间的关系(边)集合。

1.2 相关概念

  • 1 度:与该定点相连边的条数。对于有向图来说,顶点的出边条数为该顶点的出度,入边条数为该定点的入度。

2 创建

2.1 邻接矩阵

定义:对于二维数组​​G[N][N]​​​两维分别表示图的顶点标号,即如果​​G[i][j]​​​为1,说明顶点i和顶点j之间有边(对于有向图:存在从顶点i到顶点j的边);如果​​G[i][j]​​为0,说明顶点i和j之间不存在边。

int G[][];//邻接矩阵

注意:如果顶点数目太大,便可能会超过题目限制的内存。

  • 因此邻接矩阵只适用与顶点数目不太大(不超过1000)的题目。
  • 对于不存在的边可以设边权为0,-1或一个很大的数。


2.2 邻接表

2.2.1 概念

定义:设图G(V,E)的顶点编号为​​0,1,...,N-1,​​每个顶点都可能有若干条出边,如果把同一个顶点的所有出边放在一个列表中,那么N个顶点就会有N个列表(没有出边,则对应空表)。这N个列表被称为图G的邻接表,记为Adj[N]。

其中,Adj[i]存放顶点i的所有出边组成为列表,这样Adj[0],Adj[1],…,Adj[N-1]就都是一个列表。
实现:可以用链表或者vector数组实现

2.2.2 实现

2.2.2.1 如果只存放每条边的终点编号,不存放边权

vector<int> Adj[N];

如果要添加一条从1号顶点到3号顶点的有向边:

vector[1].push_back(3);

2.2.2.2 如果同时存放终点编号和边权

struct Node{
int v;//边的终点编号
int w;//边权
};
vector<Node> Adj[N];

如想添加一条从1号到3号的有向边,边权为4:

Node temp;
temp.v = 3;
temp.w = 4;
Adj[1].push_back(temp);
  • 一种更快的方法,定义结构体Node的构造函数:
struct Node{
int v, w;
Node(int _v, int _w) : v(_v), w(_w){} //构造函数
};
vector<Node> Adj[N];

添加一条从1号到3号的有向边,边权为4

Adj[1].push_back(Node(3, 4));

3 遍历

3.1 DFS

3.1.1 通用模版(伪代码)

void DFS(u){//访问顶点u
vis[u] = true;//设置u已被访问
for (从u出发能到达的所有顶点v)//枚举从u出发可以到达的所有顶点v
if vis[v] == false //如果v未被访问
DFS(v);
}

DFSTrave(G){//遍历图G
for (G的所有顶点u) //对G的所有顶点u
if vis[u] = false //如果u未被访问
DFS(u); //访问u所在的连通块
}

3.1.2 邻接矩阵

const int MAXV = 1000;//最大顶点数
const int INF = 1000000000;//设INF为一个很大的数

//邻接矩阵
int n ,G[MAXV][MAXV];//n为顶点数,MAXV为最大顶点数
bool vis[MAXV] = {false};//如果顶点i已被访问,则vis[i]==true,初始值为false

void DFS(int u,int depth){//u:当前访问的顶点标号,depth为深度
vis[u] = true; //设置u已被访问
//如果需要对u进行操作,可以在这里进行
//下面对所有从u出发能到达的分支顶点进行枚举
for (int v = 0; v < n; ++v)//对于每个顶点v
{
if(vis[v] == false && G[u][v] != INF){//如果v未被访问,且u可以到达v
DFS(v, depth + 1);//访问v,深度加1
}
}
}

void DFSTrave(){//遍历图G
for (int u = 0; u < n; ++u)//对每个顶点u
{
if(vis[u] == false){//如果u未被访问
DFS(u, 1); //访问u和u所在的连通块,1表示初始为第一层
}
}
}

3.1.3 邻接表

const int MAXV = 1000;//最大顶点数
const int INF = 1000000000;//设INF为一个很大的数

vector<int> Adj[MAXV];//图G的邻接表
int n;//n为顶点数,MAXV为最大顶点数
bool vis[MAXV] = {false};//如果顶点i已被访问过,则vis[i] == true,初始值为false

void DFS(int u, int depth){//u为当前访问的顶点标号,depth为深度
vis[u] = true;//设置u已被访问
//如需对u进行操作,可以在此进行

for (int i = 0; i < Adj[u].size(); ++i)//对从u出发所有可到达的顶点v
{
int v = Adj[u][i];
if(vis[v] == false){//如果v未被访问
DFS(v, depth + 1);//访问v,深度加1
}
}
}

void DFSTrave(){
for (int u = 0; u < n; ++u)//遍历图G
{
if(vis[u] == false){//如果u未被访问
DFS(u, 1);// 访问u和u所在的连通块,1表示初始为第一层
}
}
}

3.2 BFS

3.2.1 模版伪代码

void BFS(u){//遍历u所在的连通块
queue q;//定义队列q
将u入队
inq[u] = true;//设置u已被加入过队列
while(q非空){//只要队列非空
取出q的队首元素u并访问
for (从u出发可达的所有顶点v)//枚举从u可达的所有顶点v
{
if(inq[v] == false){//如果v未曾加入多队列
将v入队
inq[v] = true;//设置v已被加入过队列
}
}
}
}

void BFSTravel(G){//遍历图G
for (G的所有顶点u)//枚举G的所有顶点u
{
if(inq[u] == false){//如果u未曾入队
BFS(u);//遍历u所在的连通块
}
}
}

3.2.2 邻接矩阵

const int MAXV = 1000;//最大顶点个数
int n, G[MAXV][MAXV];//n:顶点个数,G:邻接矩阵
bool inq[MAXV] = {false};//如果顶点i已被访问过,则inq[i] == true,初始值为false

int INF = 1000000000;

void BFS(int u){//遍历u所在的连通块
queue<int> q;//定义队列q
q.push(u); //将初始点u入队
inq[u] = true; //设置u已被加入队列
while(!q.empty()){//只要队列非空
int u = q.front();//取出队首元素
q.pop(); //将队首元素出队

for (int v = 0; v < n; ++v)
{
if(inq[v] == false && G[u][v] != INF){//如果u的邻接点v未曾入队
q.push(v);//将v入队
inq[v] = true;//将v标记为已加入队列
}
}
}
}

void BFSTravel(){//遍历图G
for (int u = 0; u < n; ++u)//枚举所有顶点
{
if(inq[u] == false){//如果u未曾入队
BFS(u); //遍历u所在的连通块
}
}
}

3.2.3 邻接表

const int MAXV = 1000;
vector<int> Adj[MAXV];
int n;
bool inq[MAXV] = {false};

void BFS(int u){
queue<int> q;
q.push(u);
inq[u] = true;
while(!q.empty()){
int u = q.front();
q.pop();

for (int i = 0; i < Adj[u].size(); ++i)
{
int v = Adj[u][i];
if(inq[v] == false){
q.push(v);
inq[v] = true;
}
}
}
}

void BFSTravel(){
for (int u = 0; u < n; ++u)
{
if(inq[u] == false){
BFS(u);
}
}
}
  • 优化,如果需要输出连通块内其他所有顶点的层号
struct Node
{
int v;//编号
int layer;//顶点层号
};

const int MAXV = 1000;
vector<Node> Adj[MAXV];
int n;
bool inq[MAXV] = {false};



void BFS(int u){
queue<Node> q;
Node start;
start.v = u;
start.layer = 0;//起始顶点层号为0
q.push(start);//将起始顶点压入队列
inq[start.v] = true;//起始顶点的编号设置为已入队

while(!q.empty()){
Node topNode = q.front();//取出队首顶点
q.pop(); //队首顶点出队
int u = topNode.v; //队首顶点的编号

for (int i = 0; i < Adj[u].size(); ++i)
{
Node next = Adj[u][i];//从u出发能达到的顶点next
next.layer = topNode.layer + 1;//next层号=当前层号+1

if(inq[next.v] == false){//如果next的编号未曾入队
q.push(next);
inq[next.v] = true;
}
}
}
}

void BFSTravel(){
for (int u = 0; u < n; ++u)
{
if(inq[u] == false){
BFS(u);
}
}
}