图及其遍历(邻接表)

图及其遍历(邻接表)

例题

建立无向图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;

刚学不久,这道题也写了好长时间,如果有哪里不对,还恳请各位师傅指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值