图的深度优先遍历和广度优先遍历理解
前言
根据分类,图的搜索分类可以分为
- BFS和DFS
- 记忆化搜索(基于深搜)
- 双向广搜
- 二分状态搜索
- 启发式搜索
- 与或树搜索
- 博弈树搜索(α-β剪枝)(极大极小过程搜索)
- A*搜索
- IDA搜索
先看BFS和DFS,因为这是最基础的搜索策略了,BFS是按照深度进行搜索,DFS则是按照广度进行搜索;
其实只要你理解了树的DFS和BFS,那么图的话,只是在其基础上加了判断结点是否访问过,是否联通而已;
深度优先搜索
简介
先看一幅有向图
可以发现,其遍历的顺序为
0->3->1->2->4
其的概念就是一条路走到黑
图的表示方法包括邻接表,邻接矩阵等,邻接矩阵表示的是用一个二维数组表示图的联通,其中i行j列表示了i结点和j结点的联通情况,如果其为1,说明是联通的,如果其为0,反之;邻接表表示的是一个一维数组,但是数组中每个列表包含着是链表;
看个图加深理解:
其中a是有向图/原图,b是邻接表表示图,c是邻接矩阵表示图;
伪代码
递归实现
1. 访问数组初始化:visited[n] = 0
2. 访问顶点:visited[v] = 1
3. 取v的第一个邻接点w;
4. 循环递归:
while(w存在)
if(w未被访问过)
从顶点w出发递归执行;
w = v的下一个邻接点;
非递归实现
1. 栈初始化:visited[n] = 0
2. 访问顶点:visited[v] = 1
3. 入栈
4. while(栈不为空)
x = 栈的顶元素,并且出栈;
if (存在并找到未被访问的x的邻接点w)
访问w:visited[w] = 1
w进栈
上面的寻找下一个邻接点,需要根据图是邻接表还是邻接矩阵进行循环判断;
实现
由于图的表示有几种,所以实现的代码包括了用邻接矩阵的图和邻接表的图;
.h文件
typedef struct edge {
int vertex;
struct edge* next;
}Edge;
class DFS {
public:
DFS();
~DFS();
void Test_M();
void Test_L();
private:
// 邻接矩阵
void createGraph_M(int (*edge)[VERTEXNUM], int start, int end);
void displayGraph_M(int (*edge)[VERTEXNUM]);
void DFT_M(int (*edge)[VERTEXNUM], int* vertexStatusArr);
void DFTCore_M(int (*edge)[VERTEXNUM], int i, int* vertexStatusArr);
// 邻接表
void createGraph_L(Edge** edge, int start, int end);
void displayGraph_L(Edge** edge);
void delGraph_L(Edge** edge);
void DFT_L(Edge** edge, int* vertextStatusArr);
void DFTCore_L(Edge** edge, int i, int* vertexStatusArr);
};
void DFSTest_M();
void DFSTest_L();
.cpp文件
DFS::DFS() {
}
DFS::~DFS() {
}
/*----------------------------邻接矩阵--------------------------*/
/**
* 创建图
*
* @param edge <#edge description#>
*/
void DFS::createGraph_M(int (*edge)[VERTEXNUM], int start, int end) {
edge[start][end] = 1;
}
/**
* 打印存储的图
*
* @param edge <#edge description#>
*/
void DFS::displayGraph_M(int (*edge)[VERTEXNUM]) {
for (int i = 0; i < VERTEXNUM; i++) {
for (int j = 0; j < VERTEXNUM; j++) {
std::cout << edge[i][j];
}
std::cout << std::endl;
}
}
/**
* 深度优先遍历
*
* @param edge <#edge description#>
*/
void DFS::DFT_M(int (*edge)[VERTEXNUM], int* vertexStatusArr) {
for (int i = 0; i < VERTEXNUM; i++) {
DFTCore_M(edge, i, vertexStatusArr);
}
std::cout << std::endl;
}
void DFS::DFTCore_M(int (*edge)[VERTEXNUM], int i, int* vertexStatusArr) {
if (vertexStatusArr[i] == 1) return; // 顶点访问过则不再访问
std::cout << i << "->";
vertexStatusArr[i] = 1;
// 存在边的对应关系,才递归
for (int j = 0; j < VERTEXNUM; j++) {
if (edge[i][j] == 1) {
DFTCore_M(edge, j, vertexStatusArr);
}
}
}
void DFS::Test_M() {
std::cout << "--------------邻接矩阵表示-----------------" << std::endl;
//动态创建存放边的二维数组
int (*edge)[VERTEXNUM] = (int (*)[VERTEXNUM])malloc(sizeof(int) * VERTEXNUM * VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
for (int j = 0; j < VERTEXNUM; j++) {
edge[i][j] = 0;
}
}
// 存放顶点的遍历状态;0:未遍历,1:已遍历
int *vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM*VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
vertexStatusArr[i] = 0;
}
std::cout << "after init.." << std::endl;
displayGraph_M(edge);
//创建图
createGraph_M(edge, 0, 3);
createGraph_M(edge, 0, 4);
createGraph_M(edge, 3, 1);
createGraph_M(edge, 3, 2);
createGraph_M(edge, 4, 1);
std::cout << "after create.." << std::endl;
displayGraph_M(edge);
// 遍历
std::cout << "traversal.." << std::endl;
DFT_M(edge, vertexStatusArr);
free(edge);
}
/*----------------------------邻接表--------------------------*/
void DFS::createGraph_L(Edge** edge, int start, int end) {
Edge* nedge = (Edge*)malloc(sizeof(Edge));
nedge->vertex = end;
nedge->next = nullptr;
edge = edge + start;
while (*edge != nullptr) {
edge = &((*edge)->next);
}
*edge = nedge;
}
void DFS::displayGraph_L(Edge** edge) {
Edge* nedge;
int edgeCount = 0;
for (int i = 0; i < VERTEXNUM; i++) {
nedge = *(edge + i);
std::cout << i << ":";
while (nedge != nullptr) {
std::cout << nedge->vertex << ",";
nedge = nedge->next;
edgeCount++;
}
std::cout << std::endl;
}
std::cout <<"edge count is " << edgeCount << std::endl;
}
void DFS::delGraph_L(Edge** edge) {
Edge *p, *del;
for (int i = 0; i < VERTEXNUM; i++) {
p = *(edge + i);
while (p != nullptr) {
del = p;
p = p->next;
free(del);
}
edge[i] = nullptr;
}
free(edge);
}
void DFS::DFT_L(Edge** edge, int* vertextStatusArr) {
for (int i = 0; i < VERTEXNUM; i++) {
DFTCore_L(edge, i, vertextStatusArr);
}
std::cout << std::endl;
}
void DFS::DFTCore_L(Edge** edge, int i, int* vertexStatusArr) {
if (vertexStatusArr[i] == 1) {
return;
}
std::cout << i << "->";
vertexStatusArr[i] = 1;
Edge *p = *(edge + i);
while (p != nullptr) {
DFTCore_L(edge, p->vertex, vertexStatusArr);
p = p->next;
}
}
void DFS::Test_L() {
std::cout << "--------------邻接表表示-----------------" << std::endl;
// 动态创建存放边的指针数组
Edge **edge = (Edge**)malloc(sizeof(Edge*)*VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
edge[i] = nullptr;
}
// 存放顶点的遍历状态:0:未遍历,1:已遍历
int *vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
vertexStatusArr[i] = 0;
}
std::cout << "after init.." << std::endl;
displayGraph_L(edge);
//创建图
createGraph_L(edge, 0, 3);
createGraph_L(edge, 0, 4);
createGraph_L(edge, 3, 1);
createGraph_L(edge, 3, 2);
createGraph_L(edge, 4, 1);
std::cout << "after create.." << std::endl;
displayGraph_L(edge);
// 深度优先遍历
DFT_L(edge, vertexStatusArr);
// 释放邻接表占用的内存
delGraph_L(edge);
edge = nullptr;
free(vertexStatusArr);
vertexStatusArr = nullptr;
}
总结:代码有很强的C的风格,抱歉;这里面主要看遍历过程;
广度优先遍历
简介
先看一幅有向图
可以发现,其遍历的顺序为
0->3->4->1->2
其的概念就是左看看右看看,雨露均沾
伪代码
非递归实现
1. 初始化队列:visited[n] = 0
2. 访问顶点:visited[v] = 1
3. 顶点v加入队列
4. 循环:
while(队列是否为空)
v = 队列头元素
w = v的第一个邻接点
while(w存在)
if(如果w未访问)
visited[w] = 1;
顶点w加入队列
w = 顶点v的下一个邻接点
上面的寻找下一个邻接点,是寻找之前结点的下一个邻接点,而不是当前的,否则就变成DFS了;
实现
.h文件
typedef struct bedge {
int vertex;
struct bedge* pre;
struct bedge* next;
}BEdge;
template <class T>
class Queue {
private:
T *arr;
int front;
int rear;
int size;
int length;
public:
Queue();
~Queue();
void Push(T value);
T Pop();
int Size();
int Length();
bool Empty();
bool Full();
};
class BFS {
private:
BEdge *front;
BEdge *rear;
public:
BFS();
~BFS();
void Test_M();
void Test_L();
void createGraph_M(int (*edge)[VERTEXNUM], int start, int end);
void displayGraph_M(int (*edge)[VERTEXNUM]);
void BFT_M(int (*edge)[VERTEXNUM], int *vertexStatusArr);
void BFTCore_M(int (*edge)[VERTEXNUM], int i, int *vertexStatusArr);
void createGraph_L(BEdge** edge, int start, int end);
void displayGraph_L(BEdge** edge);
void delGraph_L(BEdge** edge);
void BFT_L(BEdge** edge, int* vertextStatusArr);
void BFTCore_L(BEdge** edge, int i, int* vertexStatusArr);
};
void BFSTest_M();
void BFSTest_L();
.cpp文件
template <class T>
Queue<T>::Queue() {
size = 10;
arr = new T[size];
front = rear = length = 0;
}
template <class T>
Queue<T>::~Queue() {
delete [] arr;
}
template <class T>
void Queue<T>::Push(T value) {
// 大于原数组长度,创建新数组,赋值给新数组
if (length == size) {
int nsize = size * 2;
int * narr = new T(nsize);
int i = 0;
for (; i < length; i++) {
narr[(front + i) % nsize] = arr[(front + i) % size];
}
rear = (front + i) % nsize;
arr = narr;
size = nsize;
}
arr[rear] = value;
rear = (rear + 1) % size;
++length;
}
template <class T>
T Queue<T>::Pop() {
T temp = arr[front];
front = (front + 1) % size; // 原来的内存块没有做到真正的删除
--length;
return temp;
}
template <class T>
int Queue<T>::Size() {
return size;
}
template <class T>
int Queue<T>::Length() {
return length;
}
template <class T>
bool Queue<T>::Empty() {
return length == 0;
}
template <class T>
bool Queue<T>::Full() {
return length == size;
}
BFS::BFS() {
}
BFS::~BFS() {
}
void BFS::createGraph_M(int (*edge)[VERTEXNUM], int start, int end) {
edge[start][end] = 1;
}
void BFS::displayGraph_M(int (*edge)[VERTEXNUM]) {
for (int i = 0; i < VERTEXNUM; i++) {
for (int j = 0; j < VERTEXNUM; j++) {
std::cout << edge[i][j];
}
std::cout << std::endl;
}
}
void BFS::BFT_M(int (*edge)[VERTEXNUM], int *vertexStatusArr) {
for (int i = 0; i < VERTEXNUM; i++) {
BFTCore_M(edge, i, vertexStatusArr);
}
std::cout << std::endl;
}
void BFS::BFTCore_M(int (*edge)[VERTEXNUM], int i, int *vertexStatusArr) {
Queue<int> queue;
queue.Push(i);
while (!queue.Empty()) {
int t = queue.Pop();
if (vertexStatusArr[t] == 0) {
std::cout << t << "->";
vertexStatusArr[t] = 1;
for (int i = 0; i < VERTEXNUM; i++) {
if (edge[t][i] == 1) {
queue.Push(i);
}
}
}
}
}
void BFS::Test_M() {
std::cout << "--------------邻接矩阵表示-----------------" << std::endl;
//动态创建存放边的二维数组
int (*edge)[VERTEXNUM] = (int (*)[VERTEXNUM])malloc(sizeof(int) * VERTEXNUM * VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
for (int j = 0; j < VERTEXNUM; j++) {
edge[i][j] = 0;
}
}
// 存放顶点的遍历状态;0:未遍历,1:已遍历
int *vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM*VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
vertexStatusArr[i] = 0;
}
std::cout << "after init.." << std::endl;
displayGraph_M(edge);
//创建图
createGraph_M(edge, 0, 3);
createGraph_M(edge, 0, 4);
createGraph_M(edge, 3, 1);
createGraph_M(edge, 3, 2);
createGraph_M(edge, 4, 1);
std::cout << "after create.." << std::endl;
displayGraph_M(edge);
// 遍历
std::cout << "traversal.." << std::endl;
BFT_M(edge, vertexStatusArr);
free(edge);
}
void BFS::createGraph_L(BEdge** edge, int start, int end) {
BEdge* nedge = (BEdge*)malloc(sizeof(BEdge));
nedge->vertex = end;
nedge->next = nullptr;
edge = edge + start;
while (*edge != nullptr) {
edge = &((*edge)->next);
}
*edge = nedge;
}
void BFS::displayGraph_L(BEdge** edge) {
BEdge* nedge;
int edgeCount = 0;
for (int i = 0; i < VERTEXNUM; i++) {
nedge = *(edge + i);
std::cout << i << ":";
while (nedge != nullptr) {
std::cout << nedge->vertex << ",";
nedge = nedge->next;
edgeCount++;
}
std::cout << std::endl;
}
std::cout <<"edge count is " << edgeCount << std::endl;
}
void BFS::delGraph_L(BEdge** edge) {
BEdge *p, *del;
for (int i = 0; i < VERTEXNUM; i++) {
p = *(edge + i);
while (p != nullptr) {
del = p;
p = p->next;
free(del);
}
edge[i] = nullptr;
}
free(edge);
}
void BFS::BFT_L(BEdge** edge, int* vertextStatusArr) {
for (int i = 0; i < VERTEXNUM; i++) {
BFTCore_L(edge, i, vertextStatusArr);
}
std::cout << std::endl;
}
void BFS::BFTCore_L(BEdge** edge, int i, int* vertexStatusArr) {
Queue<int> queue;
queue.Push(i);
while (!queue.Empty()) {
int t = queue.Pop();
if (vertexStatusArr[t] == 0) {
std::cout << t << "->";
vertexStatusArr[t] = 1;
BEdge* p = *(edge + t);
while (p != nullptr) {
queue.Push(p->vertex);
p = p->next;
}
}
}
}
void BFS::Test_L() {
std::cout << "--------------邻接表表示-----------------" << std::endl;
// 动态创建存放边的指针数组
BEdge **edge = (BEdge**)malloc(sizeof(BEdge*)*VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
edge[i] = nullptr;
}
// 存放顶点的遍历状态:0:未遍历,1:已遍历
int *vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM);
for (int i = 0; i < VERTEXNUM; i++) {
vertexStatusArr[i] = 0;
}
std::cout << "after init.." << std::endl;
displayGraph_L(edge);
//创建图
createGraph_L(edge, 0, 3);
createGraph_L(edge, 0, 4);
createGraph_L(edge, 3, 1);
createGraph_L(edge, 3, 2);
createGraph_L(edge, 4, 1);
std::cout << "after create.." << std::endl;
displayGraph_L(edge);
// 深度优先遍历
BFT_L(edge, vertexStatusArr);
// 释放邻接表占用的内存
delGraph_L(edge);
edge = nullptr;
free(vertexStatusArr);
vertexStatusArr = nullptr;
}
参考:
关注公众号:数据结构与算法那些事儿,每天一篇数据结构与算法