双向广度优先搜索

什么是双向 BFS?
双向 BFS(Bidirectional Breadth-First Search)是一种图搜索算法,它从起点和终点同时开始搜索,以求得最短的路径。相比于普通的 BFS 算法,双向 BFS 算法可以减少搜索的节点数,从而提高搜索效率。
在双向 BFS 算法中,我们使用两个队列或集合分别记录从起点和终点出发的可达节点,并在每一轮中交替地从两个队列或集合中选择一个节点进行扩展。如果某个节点在两个方向上都被访问到了,那么说明在这两个节点之间存在一条路径。

双向 BFS 的优势
与普通的 BFS 算法相比,双向 BFS 算法具有以下优势:
搜索的节点数更少。由于双向 BFS 算法从两个方向同时开始搜索,因此它的搜索深度只需要普通 BFS 算法的一半,从而减少了搜索的节点数。
搜索的速度更快。由于双向 BFS 算法从起点和终点同时开始搜索,因此它的搜索速度更快。
能够避免无效搜索。由于双向 BFS 算法是从起点和终点同时开始搜索,因此它可以避免搜索那些无效的节点。

双向 BFS 的应用场景
双向 BFS 算法常用于求解最短路径问题,例如:

单词接龙问题
迷宫问题
网络延迟时间问题
火车换乘问题
双向 BFS 的实现步骤
双向 BFS 算法的实现步骤如下:

定义两个队列Qstart和 Qend,分别用于记录从起点和终点开始的可达节点。同时,定义两个集合visitstart和 visitend,用于记录每个节点是否被访问过。

将起点加入 Qstart和 visitstart中,将终点加入 Qend和 visitend中。从Qstart和Qend中分别取出一个节点,使用 BFS 的方法进行扩展,并记录每个节点的距离和前驱节点列表。如果某个节点在两个方向上都被访问到了,那么说明在这两个节点之间存在一条路径。
如果某一方向的队列为空,则结束搜索。否则,交替从两个队列中选择一个队列进行扩展。
如果在搜索过程中发现了重复节点,需要根据具体情况决定是否将其从队列中删除。
如果搜索结束时没有找到终点,则说明不存在从起点到终点的路径。

代码演示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <iostream>
#include <queue>
#include <unordered_set>
#include <vector>
using namespace std;

// 双向广度优先搜索的辅助函数
bool searchLevel(const vector<vector<int>>& graph, queue<int>& Q, unordered_set<int>& visitedCurrent, unordered_set<int>& visitedOpposite) {
    int levelSize = Q.size();
    for (int i = 0; i < levelSize; ++i) {
        int node = Q.front();
        Q.pop();

        for (const int& neighbor : graph[node]) {
            if (visitedOpposite.count(neighbor)) {
                return true;  // 找到交汇点
            }

            if (!visitedCurrent.count(neighbor)) {
                visitedCurrent.insert(neighbor);
                Q.push(neighbor);
            }
        }
    }
    return false;
}

// 双向广度优先搜索主函数
bool bidirectionalBFS(const vector<vector<int>>& graph, int start, int end) {
    if (start == end) return true;

    // 初始化队列和集合
    queue<int> Qstart, Qend;
    unordered_set<int> visitStart, visitEnd;

    Qstart.push(start);
    visitStart.insert(start);

    Qend.push(end);
    visitEnd.insert(end);

    while (!Qstart.empty() && !Qend.empty()) {
        // 从起点方向进行一层搜索
        if (searchLevel(graph, Qstart, visitStart, visitEnd)) {
            return true;  // 找到交汇点
        }

        // 从终点方向进行一层搜索
        if (searchLevel(graph, Qend, visitEnd, visitStart)) {
            return true;  // 找到交汇点
        }
    }
    return false;  // 未找到路径
}

int main() {
    int n = 6; // 图中节点的数量
    vector<vector<int>> graph(n); // 使用邻接表表示图

    // 添加边
    graph[0].push_back(1);
    graph[1].push_back(0);
    graph[0].push_back(2);
    graph[2].push_back(0);
    graph[1].push_back(3);
    graph[3].push_back(1);
    graph[2].push_back(4);
    graph[4].push_back(2);
    graph[3].push_back(5);
    graph[5].push_back(3);
    graph[4].push_back(5);
    graph[5].push_back(4);

    int start = 0;
    int end = 5;
    if (bidirectionalBFS(graph, start, end)) {
        cout << "找到路径!" << endl;
    } else {
        cout << "未找到路径。" << endl;
    }

    return 0;
}

使用 vector<vector<int>> 作为图的表示:图的邻接表直接使用一个 vector 来表示,每个节点的邻接节点存储在一个 vector<int> 中。

bidirectionalBFS 函数:双向广度优先搜索的主要函数。它初始化两个队列(QstartQend)和两个集合(visitStartvisitEnd),并通过调用 searchLevel 函数来扩展每一层。

searchLevel 函数:用于扩展当前层的所有节点,并检查是否遇到了从对方向搜索到的节点。

主函数 main:设置图的结构和起点、终点,然后调用 bidirectionalBFS 来查找从起点到终点的路径。

Scroll to Top