摘要
本文将介绍利用哈希map进行建图操作的方法,使用C++STL:unordered_map 进行建图。该建图方法具有一定的局限性,但能适用于大部分图论的建图需求。相比于邻接表建图法,更容易理解和入门。
原理分析
选择unordered_map是由于其相对于map,查找速度更快,在遍历图时可以缩短时间复杂度。建图原理类似于邻接表,由一个元素映射一个集合。实现父节点与子节点集的对应。
该方法在定义时常搭配线性结构进行储存,如对于一般的图,可搭配vector初始化为: unordered_map<int,vector<int>> mp;
<键值> 使用int表示某个节点,<实值>使用vector储存该节点的所有子节点的集合。
建图和遍历
- 建图
定义了上述结构之后,可以以边为单位建立图结构。
假如一个图存在 4个节点5条边,具有(1,2)(1,3)(1,4)(2,3)(4,3)的结构,即
则建图时可以通过 mp[1] .push_bak(2)操作,来向节点1 对应的子节点集中添加节点2 。继续这个操作,使得mp内部存在 < 1, {2,3,4} >的数据,则完成了对节点1和其子节点的入图操作。类似的,可以将整个图储存下来。
对于测试点n个节点,m条边,建图操作的代码如下:
#include<iostream>
#include<unordered_map>
#include<vector>
using namespace std;
unordered_map<int, vector<int>> mp;
int main()
{
int n, m;
cin >> n >> m;
for (int i = 0; i < m; i++)
{
int a, b; //由a指向b的一条边
cin >> a >> b;
mp[a].push_back(b); //如果需要建立无向图,再构建mp[b].push_back(a)的关系即可;
}
}
- 遍历
如果要遍历一个点的所有子节点,可以使用迭代器的方法进行遍历。
如对于上述例图,遍历1的所有子节点。可以采用
for(auto i : mp[1])
{
cout << i; //即子节点
}
使用迭代器迭代节点1所指的vector数组中的所有元素,元素即为子节点。
由此可以看出,该遍历方法比起数组支持的邻接表建图法十分简便,且利于理解和分析记忆。
DFS遍历的代码如下:
void DFS(int u)
{
cout << u << " ";
st[u] = 1; //记录是否遍历过;
for (auto i : mp[u])
{
if (!st[i]) DFS(i); //搜索未遍历过的子点。
}
}
BFS遍历的代码如下:
void BFS(int u)
{
memset(st, 0, sizeof st);
cout << u << " ";
queue<int> q;
q.push(u);
st[u] = 1;
while (!q.empty())
{
int temp = q.front();
q.pop();
for (auto i : mp[temp]) //取队列中头元素的子节点集
{
if (!st[i])
{
cout << i << " ";
st[i]=1;
q.push(i);
}
}
}
}
优缺点分析
- 优点:
使用该方法的优点是映射关系浅显易懂,建图和遍历操作都十分简便,对于初学图论更加利于使用。在代码量上减少很多。且由于使用了STL储存,可以随意更改元素类型。
- 缺点:
由于unordered_map的创建耗时较多。所以相较于使用数组实现的邻接表建图更慢。
拓展...
如果题目要求建立带权图,那么可以更改mp数据结构的定义。
使用
unordered_map<int,vector<pair<int,int>>> mp;
在建图操作时,以a指向b的长度为c的边可以表示为
mp[a].push_back({b,c});
在遍历时,需要对vector迭代的过程中,取pair元素的first即可获得子节点,取second即可获得当前节点与子节点的距离。
for (int i = 0; i < m; i++)
{
int a, b, c;
cin >> a >> b >> c;
mp[a].push_back({ b,c });
}
void DFS(int u)
{
memset(st, 0, sizeof st);
cout << u << " ";
st[u] = 1;
for (auto i : mp[u])
{
int j = i.first;
int len = i.second;
if (!st[j]) DFS(j);
}
}
同时,哈希map也可以模拟邻接矩阵的思想进行建图,相比之下,可以储存邻接矩阵储存不了的稀疏图,对于节点数较大的图也可储存,其方法为;
unordered_map<int, unordered_map<int, int>>mp;
建图操作为 mp[a][b]=c; 可直接表示a到b的距离c;
遍历操作与vector和pair结合的结构进行的操作类似,其优点在于可以直接取任意两点:a到b的距离,通过操作mp[a].find(b)实现。其返回值如果为真,说明存在这样一条边。则查找边的时间复杂度可以降为O(1)。