图及其遍历(邻接表)
图及其遍历(邻接表)
例题
建立无向图G的邻接表存储结构,求其从任意顶点出发的广度优先搜索序列与深度优先搜索序列。(每个点优先去往周围未到达的点中编号最小的点)
输入格式:
第一行两个正整数n, m表示G中顶点数与边数
接下来m行每行两个正整数u,v表示u点与v点之间有一条无向边
若同一条边被输入两次,则自动增加一行输入 (1 <= u, v, w <= n) (1 <= n <= 500, 1 <= m <= 2000)
最后一行一个正整数w表示遍历的起始顶点
输出格式:
第一行n个正整数表示广度优先搜索序列
第二行n个正整数表示深度优先搜索序列
(若输入的起始顶点不存在,则不输出顶点搜索序列,输出“起始顶点不存在”)
样例输入:
5 10 1 2 1 3 2 4 4 5 1 3 1 4 2 5 2 3 3 5 1 5 3 4 1 (其中1 3 重复输入)
样例输出:
1 2 3 4 5 1 2 3 4 5
解题思路
首先构造领接表中的节点结构
struct AdjListNode {
int dest;
AdjListNode* next;
};
其中dest指节点的值,next指针是用来储存与该节点相连接的其他节点
之后构建领接表结构
struct AdjList {
AdjListNode* head;
};
每个节点都组成一个头结点
之后先写一个判断两个节点之间是否已经存在边的函数
bool hasEdge(vector<AdjList>& graph, int u, int v) {
AdjListNode* p = graph[u].head;
while (p != nullptr) {
if (p->dest == v) {
return true;
}
p = p->next;
}
return false;
}
先定义一个领接表节点,该节点值为u,对该节点头指针进行遍历,如果在遍历过程中发现该节点连接着值为v的节点,则说明u和v之间原来是有边的,返回true,否则返回false
之后进行构建无向图的邻接表
void createGraph(int n, int m, vector<AdjList>& graph) {
graph.resize(n + 1);
for (int i = 0; i < m; ++i) {
int u, v;
cin >> u >> v;
if (hasEdge(graph, u, v))
i--;
else {
AdjListNode* newNode = new AdjListNode{ v };
newNode->next = graph[u].head;
graph[u].head = newNode;
newNode = new AdjListNode{ u };
newNode->next = graph[v].head;
graph[v].head = newNode;
}
}
}
在以上代码中,graph对象是含有一些邻接表结构的容器,在这个容器中装的都是节点的头指针,将它扩大到可以装有n+1个元素,然后接收输入的前后节点的值,接收完之后先判断两节点之间原来是否有边,如果有,则i–后继续循环,如果没有,则为这两个节点之间加边,先让v->u,再让u->v
在无向图的邻接表构建好之后,先开始广度优先搜索
vector<int> bfs(vector<AdjList>& graph, int start) {
vector<int> bfsResult;
vector<bool> visited(graph.size(), false);
queue<int> q;
if (start < 1 || start > graph.size() - 1) {
return bfsResult;
}
visited[start] = true;
q.push(start);
while (!q.empty()) {
int curr = q.front();
q.pop();
bfsResult.push_back(curr);
// 存储当前顶点的邻接顶点
vector<int> neighbors;
AdjListNode* p = graph[curr].head;
while (p != nullptr) {
neighbors.push_back(p->dest);
p = p->next;
}
// 对邻接顶点按照编号从小到大排序
sort(neighbors.begin(), neighbors.end());
for (int neighbor : neighbors) {
if (!visited[neighbor]) {
visited[neighbor] = true;
q.push(neighbor);
}
}
}
return bfsResult;
}
首先,先定义几个结构,bfsResult用来储存广度优先搜索后的序列,visited用来储存与节点数相同的布尔值,如果该节点已经被遍历了,则相对应的布尔值就改为true,再构建一个队列结构,用来临时储存广度优先搜索过程中的节点,接下来开始正式广度优先搜索
如果用户输入的开始遍历的结点不符合要求,直接返回空的bfsResult
如果符合要求
现将该节点的布尔值设为true,再将该节点的值加入队列,接下来进入循环,如果队列不为空,先将队列中的最前面的数据读取出来赋值给curr,然后再将curr装入bfsResult,然后再定义一个局部结构neighbors,用来储存当前节点的邻接节点,然后开始遍历当前节点的所有邻接节点,只要不为空,就全部装进neighbors,之后对这些邻接节点从小到大排序,再讲这些邻接节点一个一个装进队列,然后回到循环检查队列是否为空,而此时队列中全是上一节点的邻接节点,然后开始一个一个和前面一样判断这些邻接节点,直到所有节点都被遍历结束,则广度优先搜索遍历结束
之后就进行深度优先遍历
先写一个深度优先遍历的辅助函数
void dfsUtil(vector<AdjList>& graph, int curr, vector<bool>& visited, vector<int>& dfsResult) {
visited[curr] = true;
dfsResult.push_back(curr);
// 存储当前顶点的邻接顶点
vector<int> neighbors;
AdjListNode* p = graph[curr].head;
while (p != nullptr) {
neighbors.push_back(p->dest);
p = p->next;
}
// 对邻接顶点按照编号从小到大排序
sort(neighbors.begin(), neighbors.end());
for (int neighbor : neighbors) {
if (!visited[neighbor]) {
dfsUtil(graph, neighbor, visited, dfsResult);
}
}
}
参数列表中有个当前节点的值和检查是否成功遍历的visited容器结构,还有储存深度优先遍历结果的dfsResult结构,因此现将该节点对应的布尔值改为true,然后装进dfsResult中,和前面广度优先遍历一样,
之后构建一个容器,用来储存当前curr的邻接节点,然后从小到大排序,然后对每个邻接节点进行深度优先遍历,进行递归调用,
之后正式开始进行深度优先遍历
vector<int> dfs(vector<AdjList>& graph, int start) {
vector<int> dfsResult;
vector<bool> visited(graph.size(), false);
if (start < 1 || start > graph.size() - 1) {
return dfsResult;
}
dfsUtil(graph, start, visited, dfsResult);
return dfsResult;
}
得到dfsResult结果
之后编写main函数,进行运行
int main() {
int n, m;
cin >> n >> m;
vector<AdjList> graph;
createGraph(n, m, graph);
int start;
cin >> start;
vector<int> bfsSeq = bfs(graph, start);
if (bfsSeq.empty()) {
cout << "起始顶点不存在";
}
else {
for (int num : bfsSeq) {
cout << num << " ";
}
cout << endl;
}
vector<int> dfsSeq = dfs(graph, start);
if (!dfsSeq.empty()) {
for (int num : dfsSeq) {
cout << num << " ";
}
}
return 0;
}
先构建邻接表节点,再调用函数构建无向图的邻接表结构,之后依次调用广度优先遍历函数和深度优先遍历函数,得到最终结果
运行结果示例
案列
5 10
1 2
1 3
2 4
4 5
1 4
2 5
2 3
3 5
1 5
3 4
1
输出
1 2 3 4 5
1 2 3 4 5
库名
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
刚学不久,这道题也写了好长时间,如果有哪里不对,还恳请各位师傅指正!