图的遍历的两种方法
广度优先(Breadth First Search)
像水波一样一层一层由内到外扩散访问
核心思想:先被访问的顶点,其邻接点也先被访问
队列的思想:先进先出 notice: 「出队列的过程」 == 「广度优先遍历的过程」
深度优先(DFS)
一条路走到黑,如果走不通就返回一开始的位置走下一条路,
如果走得通就走出去了,其他的路也不管了
核心思想:后被访问的顶点,其第一个邻接点先被访问
栈的思想:后进先出 notice: 「入栈的过程」 == 「深度优先遍历的过程」
有向图eg:
b
↗ ↘
a → c 5个顶点,7条边
↓ ↙ ↓
e ← d
Vex[] = a b c d e a:0 b:1 c:2 d:3 e:4
a b c d e
Egde[i][j] = a 0 1 1 0 1
b 0 0 1 0 0
c 0 0 0 1 1
d 0 0 0 0 1
e 0 0 0 0 0
邻接矩阵的广度优先算法:
1.初始化一个空队列[ ]
2.顶点a入队列[a| ],标记visited[0] = 1
3.a出队列,找a的邻接点,且未被访问过的,发现有b、c、e, notice: 相当于走ab ac ae
4.邻接点入队列[b|c|e ],标记visited[1]、[2]、[4] = 1
5.b出队列,找b的邻接点,且未被访问过的,发现无,剩余:[c|e ]
6.c出队列,找c的邻接点,且未被访问过的,发现有d
7.邻接点d入队列:[e|d ],标记visited[3] = 1
8.e出队列,找e的邻接点,且未被访问过的,发现无,剩余:[d| ]
9.d出队列,找d的邻接点,且未被访问过的,发现无,剩余:[ ]
综上:notice: 所有头指针指向的内容「出列队的过程」 == 「广度优先遍历的过程」
真正入队列的时候,abcde入队列太麻烦,让其下标入队列
判断某个顶点是否有邻接点是,只需判断矩阵某行某列[i][j]是否等于1
伪代码
1.创建一个数组用于表示顶点是否被访问过,visited[n],初始化为0,代表所有顶点未被访问
2.创建空队列,用于存放顶点下标
3.从图的起点(下标为0)出发,标记访问过,将顶点入队列
4.判断如果队列是否为空,若不为空则继续访问,否则结束
5.顶点(头部)出队列后,依次访问该顶点 [未被访问过的][所有邻接点],标记并入队列
6.返回4
输入:
ab ac ae bc cd ce de
输出:
a b c e d
#include <iostream>
#include "directed_mat.h"
using namespace std;
int main()
{
// 无向图的改为un_graph即可
struct AMG_Graph* d_graph;
d_graph = Create_AMG_Graph();
Show_AMG_Graph(d_graph);
cout << "使用广度优先遍历这张图:" << endl;
BFS_AMG(d_graph);
return 0;
}
directed_mat.cpp
#include "queue.h"
#include "directed_mat.h"
#include <iostream>
using namespace std;
struct AMG_Graph* Create_AMG_Graph()
{
int i, j;
char u, v;
struct AMG_Graph* graph;
// 申请内存空间,结构体多大申请多大,强制类型转换
graph = (struct AMG_Graph *)malloc(sizeof(struct AMG_Graph));
cout << "请输入顶点的个数: ";
cin >> graph->vex_num;
cout << "请输入边的个数: ";
cin >> graph->edge_num;
cout << "请输入顶点信息: " << endl;
for(i = 0; i < graph->vex_num; i++)
{
// getchar会捕获回车
cin >> graph->Vex[i];
}
for(i = 0; i < graph->vex_num; i++)
{
for(j = 0; j < graph->vex_num; j++)
{
// 初始化为0
graph->Edge[i][j] = 0;
}
}
while(graph->edge_num--)
{
cout << "请输入通过边连接起来的顶点:" << endl;
cin >> u;
cin >> v;
// 到graph中找到字符u,所对应的索引
i = search_vex(graph, u);
j = search_vex(graph, v);
if(i != -1 && j != -1)
// 有向图:
graph->Edge[i][j] = 1;
// 无向图:
//graph->Edge[i][j] = graph->Edge[j][i] = 1;
else
{
cout << "你输入的顶点信息是错的,请再次输入" << endl;
graph->edge_num++;
}
}
return graph;
}
void Show_AMG_Graph(struct AMG_Graph * graph)
{
int i,j;
cout << "显示顶点信息:"<< endl;
for(i = 0; i < graph->vex_num; i++)
cout << graph->Vex[i] << "\t";
cout << endl;
cout << "显示邻接矩阵:" << endl;
for(i = 0; i < graph->vex_num; i++)
{
for(j = 0; j < graph->vex_num; j++)
{
cout << graph->Edge[i][j] << "\t";
}
cout << endl;
}
}
int search_vex(struct AMG_Graph* graph, char c)
{
int i;
for(i = 0; i < graph->vex_num; i++)
{
if(c == graph->Vex[i])
return i;
}
return -1;
}
// 广度优先遍历邻接矩阵
void BFS_AMG(struct AMG_Graph* graph)
{
// 出队列的下标
int u;
// step1 创建一个数组用于表示顶点是否被访问过
int visited[MAX] = {0};
// step3 从图的起点(下标为0)出发,标记访问过,将顶点(下标)入队列
visited[0] = 1;
inqueue(0);
// step4 判断如果队列是否为空,若不为空则继续访问,否则结束 o(n²)
while(!isempty())
{
// step5 顶点(头部)出队列后,依次访问该顶点[未被访问过的][所有邻接点],标记并入队列
u = outqueue();
// notice: 出队列的过程 == 广度优先遍历的过程
cout << graph->Vex[u] << " ";
// 遍历当前出队列节点所在的二维矩阵的行
for(int i = 0; i < graph->vex_num; i++)
{
// notice: ①如果有邻接点 并且 ②没有被标记
if( (graph->Edge[u][i]==1) && (visited[i] == 0) )
{
// 标记为访问过,防止节点重复添加到队列中
visited[i] = 1;
// 邻接点下标入队列
inqueue(i);
}
}
}
cout << endl;
}
directed_mat.h
#ifndef __directed_mat_h__
#define __directed_mat_h__
#define MAX 100
// 图
struct AMG_Graph
{
// 顶点的个数,边的个数
int vex_num, edge_num;
// 储存顶点的信息,一维顶点数组
char Vex[MAX];
// 储存顶点和顶点之间的邻接关系,二维邻接矩阵
int Edge[MAX][MAX];
};
struct AMG_Graph* Create_AMG_Graph(void);
void Show_AMG_Graph(struct AMG_Graph * graph);
int search_vex(struct AMG_Graph* graph, char c);
int search_vex(struct AMG_Graph* graph, char c);
void BFS_AMG(struct AMG_Graph* graph);
#endif