数据结构 —— 图的遍历
- BFS(广度遍历)
- 一道美团题
- DFS(深度遍历)
我们今天来看图的遍历,其实都是之前在二叉树中提过的方法,深度和广度遍历。
在这之前,我们先用一个邻接矩阵来表示一个图:
#pragma once
#include<iostream>
#include<vector>
#include<map>
using namespace std;//图的模板化
namespace matrix
{template<class V, class W, W MAX_W, bool Direction = false>class Graph{public:Graph(const V* vertex, size_t n){//开辟空间_vertex.reserve(n);for (int i = 0; i < n; i++){_vertex.push_back(vertex[i]);_index[vertex[i]] = i;}//初始化矩阵_matrix.resize(n);for (auto& e : _matrix){e.resize(n, MAX_W);}}//寻找图中的点size_t FindSrci(const V& vertex){auto ret = _index.find(vertex);if (ret != _index.end()){return ret->second;}else{throw invalid_argument("不存在的顶点");return -1;}}void _AddEdge(size_t srci, size_t desi, W w){_matrix[srci][desi] = w;if (Direction == false){_matrix[desi][srci] = w;}}//加边void AddEdge(const V& srci, const V& desi, W w){size_t srcIndex = FindSrci(srci);size_t desIndex = FindSrci(desi);_AddEdge(srcIndex, desIndex, w);}void Print(){//打印标题行cout << " ";for (int i = 0; i < _vertex.size(); i++){cout << _vertex[i] << " ";}cout << endl;//打印矩阵for (int i = 0; i < _vertex.size(); i++){cout << _vertex[i] << " ";for (int j = 0; j < _matrix[i].size(); j++){if (_matrix[i][j] != MAX_W){printf("%5d", _matrix[i][j]);}else{printf("%5c", '#');}}cout << endl;}}private://存放顶点vector<V> _vertex;//映射关系map<V, size_t> _index;//矩阵,图的表示vector<vector<W>> _matrix;};void TestGraph(){Graph<char, int, INT_MAX, true> g("0123", 4);g.AddEdge('0', '1', 1);g.AddEdge('0', '3', 4);g.AddEdge('1', '3', 2);g.AddEdge('1', '2', 9);g.AddEdge('2', '3', 8);g.AddEdge('2', '1', 5);g.AddEdge('2', '0', 3);g.AddEdge('3', '2', 6);g.Print();}void TestGraph2(){string a[] = {"海皇","高斯","小傲","小潮","胖迪","小杨","皖皖"};Graph<string, int,INT_MAX, false> g1(a, sizeof(a)/sizeof(a[0]));g1.AddEdge("小潮", "小傲", 30);g1.AddEdge("小潮", "高斯", 83);g1.AddEdge("小潮", "海皇", 34);g1.AddEdge("胖迪", "海皇", 78);g1.AddEdge("胖迪", "小傲", 76);g1.AddEdge("小杨", "皖皖", 54);g1.AddEdge("小杨", "高斯", 48);g1.Print();}
}
BFS(广度遍历)
广度优先遍历和之前二叉树的广度优先遍历一样,我们需要队列来辅助:
我们逻辑上是这张图,但是我们实际遍历的时候,我们遍历的是一个矩阵(二维数组)
因为这个图是联通图,我们从任意一个顶点出发都可以遍历完所有顶点,假设我们从小潮开始:
我们仿照二叉树那里的思路,把相连的节点入队列,把小傲,高斯,海皇入队列:
在矩阵上的表现就是把小潮这一行,不为#的数值的结点入队列:
好,现在有一个问题,假设我现在遍历到了海皇,海皇和小潮相连,按照上面的规则,我们应该把小潮入队列,但是我们一开始就是从小潮开始的,就会造成重复访问,这该则么办呢?
所以在图这里,我们要引入一个数组,标记我们已经访问过的结点,标记过的结点在之后的访问过程中不再入队:
我们来实现一下:
void BFS(const V& vertex){int vertexIndex = FindSrci(vertex);//队列和标记数组int n = _vertex.size();queue<size_t> q;vector<bool> visited(n, false);//先入最先访问节点q.push(vertexIndex);//标记visited[vertexIndex] = true;while (!q.empty()){//出队size_t front = q.front();q.pop();cout << _vertex[front] << " ";//遍历该行for (int i = 0; i < _vertex.size(); i++){if (_matrix[front][i] != MAX_W){if (visited[i] == false){q.push(i);visited[i] = true;}}}cout << endl;}}
一道美团题
读了题之后,这里让我们计算几度好友,其实我们在BFS已经按照一度二度的顺序打印了,但是我们没有区分,我们区分一下就可以了
void BFS(const V& vertex)
{// 获取目标顶点的索引int vertexIndex = FindSrci(vertex);// 初始化队列和标记数组int n = _vertex.size();queue<size_t> q; // 创建一个队列用于存储待访问的顶点vector<bool> visited(n, false); // 创建一个布尔数组,用于标记顶点是否已被访问// 将起始顶点添加到队列中,并标记为已访问q.push(vertexIndex);visited[vertexIndex] = true;int levelSize = 1; // 初始化当前层级的顶点数量,初始时只有起始顶点int degree = 0; // 初始化度数(即好友的层次)// 当队列非空时,继续广度优先搜索while (!q.empty()){// 输出当前度数的好友cout << "[" << degree << "]" << "度好友 ";// 对当前层级的所有顶点进行处理for (int j = 0; j < levelSize; j++){// 从队列中取出一个顶点size_t front = q.front();q.pop();// 输出当前顶点的信息cout << _vertex[front] << " ";// 遍历与当前顶点相邻的所有顶点for (int i = 0; i < _vertex.size(); i++){// 如果存在一条边连接当前顶点和下一个顶点if (_matrix[front][i] != MAX_W){// 如果下一个顶点未被访问过if (!visited[i]){// 将下一个顶点添加到队列中,以便后续访问q.push(i);// 标记下一个顶点为已访问visited[i] = true;}}}}// 更新下一层级的顶点数量levelSize = q.size();// 换行输出,准备进入下一度数的好友输出cout << endl;// 增加度数计数器degree++;}// 最终换行,使输出更清晰cout << endl;
}
DFS(深度遍历)
深度遍历是一条道走到黑:
// 深度优先搜索的私有辅助函数
void _DFS(size_t vertexIndex, vector<bool>& visited)
{// 如果当前顶点尚未访问过if (!visited[vertexIndex]){// 输出当前顶点的值cout << _vertex[vertexIndex] << endl;// 将当前顶点标记为已访问visited[vertexIndex] = true;// 遍历所有顶点for (size_t i = 0; i < _vertex.size(); i++){// 如果当前顶点和遍历到的下一个顶点之间存在边,// 表示为权重不是最大可能权重(MAX_W),// 则对下一个顶点进行深度优先搜索if (_matrix[vertexIndex][i] != MAX_W)_DFS(i, visited);}}
}// 公开的深度优先搜索函数,从给定的顶点开始
void DFS(const V& vertex)
{// 查找给定顶点在顶点列表中的索引size_t vertexIndex = FindSrci(vertex);// 初始化一个布尔向量来跟踪每个顶点是否已被访问vector<bool> visited(_vertex.size(), false);// 调用私有辅助函数进行深度优先搜索_DFS(vertexIndex, visited);
}
(注:本人是小潮院长粉丝,该文章中举的例子不代表真实好友关系,无意挑拨小潮院长内部成员关系,如有冒犯,请不要打我…)