双向广度优先搜索的三种实现方式

📌 单队列交替扩展法

这是最早期的极简实现,将起点和终点同时放入同一个队列,正向和逆向搜索交替从队列头部取节点扩展,直到出现重复节点即终止。

✅ ‌优点‌

代码实现极其简洁,仅需维护1个队列和1个全局访问标记数组,上手门槛极低,内存占用最小,不需要额外开辟第二份队列空间;

‌缺点‌

两个方向的扩展顺序完全随机,极易出现单侧搜索树快速膨胀的情况,效率不稳定,无法精准记录两个方向各自的搜索步数,路径重建逻辑非常容易出错;

推荐星级:⭐⭐
🔍 适用场景:仅适合状态空间极小、分支因子极低的入门级练习题。

📋 例题:P1032 [NOIP 2002 提高组] 字串变换

 

 1 #include<bits/stdc++.h>
 2 #include<iostream>
 3 using namespace std;
 4 string A,B;
 5 
 6 int n=0;
 7 string a[10],b[10];
 8 unordered_map< string,pair<int,int> > mp; // < 字符串, <最小步数, 方向> > 
 9 
10 void bfs(){
11     queue<string> q;
12     q.push(A);
13     q.push(B);
14     mp[A] = make_pair(0,1);
15     mp[B] = make_pair(0,2);
16     while( q.size() ){
17         string f = q.front(); q.pop();
18         for( int i=0;i<n;i++ ){
19             size_t index =  0 ;
20             while(true ) {
21                 string ts = f;
22                 if( mp[ts].first >= 10 ) continue; 
23                 index =  f.find( a[i] , index ); 
24                 if( index == string::npos ) break;                
25                 ts.replace( index , a[i].length() , b[i] );
26                 if( mp.count(ts)==0 ){
27                     q.push( ts );
28                     mp[ts].first = mp[f].first + 1;
29                     mp[ts].second = mp[f].second ;
30                 } 
31                 else if( mp[ts].second != mp[f].second ){
32                     cout << mp[ts].first + mp[f].first + 1;
33                     return ;
34                 }        
35                 index += a[i].length();
36                 if( index + a[i].length() > f.length() ) break;        
37             }
38         }
39     }
40     cout << "NO ANSWER!";
41 } 
42 
43 int main(){
44     cin>>A>>B;
45     while( cin>>a[n] ){
46         cin>>b[n++]; 
47     }
48     bfs(); 
49     return 0;
50 }

 

 

 


 

📌 双队列等层交替扩展法

这是最经典的常规实现,分别维护正向、逆向两个独立队列,每次完整扩展完正向的一整层节点,再切换到逆向扩展一整层,交替循环直到相遇。

✅ ‌优点‌
逻辑清晰直观,两个方向的步数独立维护,相遇时能直接通过步数相加得到最短路径长度,完全保留BFS的层序特性,天然保证最短路径的最优性,路径重建难度低;
❌ ‌缺点‌
没有考虑两个方向的节点生成速度差异,若其中一侧分支因子远大于另一侧,会出现单侧队列爆炸,性能退化为接近单向BFS,固定交替的策略无法适配非完全树的场景,容易做大量无效扩展;
推荐星级:⭐⭐⭐⭐

🔍 适用场景:图结构对称、两个方向分支因子相近的场景,比如标准迷宫寻路、规则对称的拼图游戏。

📋 例题:P1746 离开中山路

 1 #include<iostream>
 2 #include<queue>
 3 using namespace std;
 4 int dx[]{1,-1,0,0} , dy[]{0,0,1,-1};
 5 char maze[1005][1005];
 6 bool vis1[1005][1005],vis2[1005][1005];
 7 int n,x1,y1,x2,y2;
 8 struct node{
 9     int x,y;
10 };
11 
12 int bfs(){
13     if( x1==x2 && y1==y2 ) return 0;
14     queue< node > q1 , q2 ;    
15     q1.push( {x1,y1} ); vis1[x1][y1] = 1;
16     q2.push( {x2,y2} ); vis2[x2][y2] = 1;    
17     int step = 0;
18     while( q1.size() && q2.size() ){
19         step++;
20         int size1 = q1.size() ;
21         while( size1-- ){
22             int x = q1.front().x , 
23                 y = q1.front().y ;
24             q1.pop();
25             for( int i=0;i<4;i++ ){
26                 int tx = x + dx[i],
27                     ty = y + dy[i];
28                 if( tx<1 || tx>n || ty<1 || ty> n) continue;
29                 if( maze[tx][ty]=='1' ) continue;
30                 if( vis2[tx][ty] ) return step;
31                 if( vis1[tx][ty] ) continue;
32                 q1.push( {tx,ty} );
33                 vis1[tx][ty] = 1;
34             }
35         }
36         step++;
37         int size2 = q2.size() ;
38         while( size2-- ){
39             int x = q2.front().x , 
40                 y = q2.front().y ;
41             q2.pop();
42             for( int i=0;i<4;i++ ){
43                 int tx = x + dx[i],
44                     ty = y + dy[i];
45                 if( tx<1 || tx>n || ty<1 || ty> n) continue;
46                 if( maze[tx][ty]=='1' ) continue;
47                 if( vis1[tx][ty] ) return step;
48                 if( vis2[tx][ty] ) continue;
49                 q2.push( {tx,ty} );
50                 vis2[tx][ty] = 1;
51             }
52         }
53     }
54     return -1;
55 }
56 
57 
58 int main(){
59     ios::sync_with_stdio(false);cin.tie(0);
60     cin>>n;
61     for(int i=1;i<=n;i++){
62         for(int j=1;j<=n;j++){
63             cin>>maze[i][j];
64         }
65     }
66     cin>>x1>>y1>>x2>>y2;
67     cout << bfs();
68     return 0;
69 }

 

📌 优先扩展小队列动态平衡法

这是目前工业界和信奥竞赛中最常用的优化实现,每次动态比较两个队列的当前节点数,优先选择规模更小的一侧进行扩展,始终保持两个方向的搜索树规模接近。

‌优点‌
从根本上解决了两个方向扩展速度不平衡的问题,能最大化发挥双向BFS的指数级效率优势,节点扩展量通常比前两种方式少30%以上;

性能表现稳定,即使在非完全树、分支因子不对称的场景下,也能避免单侧队列膨胀
❌ ‌缺点‌
实现复杂度最高,需要额外增加队列大小判断逻辑,调试时需要同时跟踪两个方向的扩展状态;
极端场景下(比如其中一个方向完全没有分支),动态切换的额外判断开销会小幅抵消性能收益;
推荐星级:⭐⭐⭐⭐⭐
🔍 适用场景:几乎所有双向BFS的生产级场景,比如单词接龙、八数码问题、社交网络最短路径查询等状态空间较大的任务。

📋 例题:P1379 八数码难题

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 string s, t = "123804765";
 4 int d[4] = {1, -1, 3, -3};
 5 unordered_set<string> vis1,vis2;
 6 int bfs(){
 7     if( s==t ) return 0;
 8     queue<string> q1,q2;
 9     q1.push(s); 
10     q2.push(t);
11     vis1.insert(s); 
12     vis2.insert(t);
13     int step = 0;
14     while( q1.size()&&q2.size() ){
15         if( q1.size() > q2.size() ){
16             swap(q1,q2);
17             swap(vis1,vis2);
18         }
19         step++;
20         int sz = q1.size();
21         while(sz--){
22             string f = q1.front(); q1.pop();
23             int c = f.find('0');
24             for(int i = 0; i < 4; i++){
25                 int t = d[i] + c;
26                 string fs = f;
27                 if(t < 0 or t > 8) continue;
28                 else if( i==0 && c%3==2  ) continue; 
29                 else if( i==1 && c%3==0  ) continue;
30                 swap(fs[t], fs[c]);
31                 if( vis2.count(fs) ) return step;
32                 if( vis1.count(fs) ) continue;
33                 q1.push(fs);
34                 vis1.insert(fs);
35             }            
36         }
37     } 
38     return -1;
39 }
40 int main(){
41     cin >> s;
42     cout << bfs() << endl;
43     return 0;
44 } 

 

⚖️bfs不同实现方式的时间对比

例题:P1032 [NOIP 2002 提高组] 字串变换

 1 #include<bits/stdc++.h>
 2 #include<iostream>
 3 using namespace std;
 4 string A,B;
 5 
 6 int n=0;
 7 string a[10],b[10];
 8 map<string,int> step; //step:记录s的步数
 9 
10 void bfs(){
11     queue<string> q;
12     q.push(A);
13     step[A] = 0;
14     while( q.size() ){
15         string f = q.front();q.pop();
16         if( f==B ) {
17             cout << step[B] ;
18             return ; 
19         }
20         for( int i=0;i<n;i++ ){
21             size_t index =  0 ;
22             while(true ) {
23                 string ts = f;
24                 index =  f.find(a[i],index ); 
25                 if( index==string::npos ) break;                
26                 ts.replace( index,a[i].length(),b[i] );
27                 if( step.count(ts)==0 ){
28                     q.push( ts );
29                     step[ts] = step[f]+1;
30                 }     
31                 index += a[i].length();
32                 if( index >= f.length()) break;        
33             }
34         }
35     }
36     cout << "NO ANSWER!";
37 } 
38 
39 int main(){
40     cin>>A>>B;
41     while( cin>>a[n] ){
42         cin>>b[n++]; 
43     }
44     bfs(); 
45     return 0;
46 }
View Code 单向bfs,运行时间:30ms
 1 #include<bits/stdc++.h>
 2 #include<iostream>
 3 using namespace std;
 4 string A,B;
 5 
 6 int n=0;
 7 string a[10],b[10];
 8 unordered_map< string,pair<int,int> > mp; // < 字符串, <最小步数, 方向> > 
 9 
10 void bfs(){
11     queue<string> q;
12     q.push(A);
13     q.push(B);
14     mp[A] = make_pair(0,1);
15     mp[B] = make_pair(0,2);
16     while( q.size() ){
17         string f = q.front(); q.pop();
18         for( int i=0;i<n;i++ ){
19             size_t index =  0 ;
20             while(true ) {
21                 string ts = f;
22                 if( mp[ts].first >= 10 ) continue; 
23                 index =  f.find( a[i] , index ); 
24                 if( index == string::npos ) break;                
25                 ts.replace( index , a[i].length() , b[i] );
26                 if( mp.count(ts)==0 ){
27                     q.push( ts );
28                     mp[ts].first = mp[f].first + 1;
29                     mp[ts].second = mp[f].second ;
30                 } 
31                 else if( mp[ts].second != mp[f].second ){
32                     cout << mp[ts].first + mp[f].first + 1;
33                     return ;
34                 }        
35                 index += a[i].length();
36                 if( index + a[i].length() > f.length() ) break;        
37             }
38         }
39     }
40     cout << "NO ANSWER!";
41 } 
42 
43 int main(){
44     cin>>A>>B;
45     while( cin>>a[n] ){
46         cin>>b[n++]; 
47     }
48     bfs(); 
49     return 0;
50 }
View Code 双向bfs,单队列交替扩展法,运行时间:28ms
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 string s, t = "123804765";
 4 int d[4] = {1, -1, 3, -3};
 5 unordered_set<string> vis1,vis2;
 6 int bfs(){
 7     if( s==t ) return 0;
 8     queue<string> q1,q2;
 9     q1.push(s);
10     q2.push(t);
11     vis1.insert(s);
12     vis2.insert(t);
13     int step = 0;
14     while( q1.size()&&q2.size() ){
15         if( q1.size() > q2.size() ){
16             swap(q1,q2);
17             swap(vis1,vis2);
18         }
19         step++;
20         int sz = q1.size();
21         while(sz--){
22             string f = q1.front(); q1.pop();
23             int c = f.find('0');
24             for(int i = 0; i < 4; i++){
25                 int t = d[i] + c;
26                 string fs = f;
27                 if(t < 0 or t > 8) continue;
28                 else if( i==0 && c%3==2  ) continue;
29                 else if( i==1 && c%3==0  ) continue;
30                 swap(fs[t], fs[c]);
31                 if( vis2.count(fs) ) return step;
32                 if( vis1.count(fs) ) continue;
33                 q1.push(fs);
34                 vis1.insert(fs);
35             }
36         }
37     }
38     return -1;
39 }
40 int main(){
41     cin >> s;
42     cout << bfs() << endl;
43     return 0;
44 }
View Code 双向bfs,优先扩展小队列动态平衡法,运行时间:19ms

 

例题:P1379 八数码难题

#include<bits/stdc++.h>
using namespace std;
string s, t = "123804765";
int d[4] = {1, -1, 3, -3};
struct node{string s; int step;};
unordered_set<string> vis;
int bfs(){
    queue<node> q;
    q.push((node){s,0});
    vis.insert(s);
    while( q.size() ){
        node f = q.front(); q.pop();
        if(f.s == t) return f.step;
        int c = f.s.find('0');
        for(int i = 0; i < 4; i++){
            int t = d[i] + c;
            string fs = f.s;
            if(t < 0 or t > 8) continue;
            else if( i==0 && c%3==2  ) continue; 
            else if( i==1 && c%3==0  ) continue;
            swap(fs[t], fs[c]);
            if( vis.count(fs) ) continue;
            q.push((node){fs, f.step + 1});
            vis.insert(fs);
        }
    } 
    return -1;
}
int main(){
    cin >> s;
    cout << bfs() << endl;
    return 0;
} 
View Code 单向bfs,运行时间:1.37s
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 string s, t = "123804765";
 4 int d[4] = {1, -1, 3, -3};
 5 unordered_set<string> vis1,vis2;
 6 int bfs(){
 7     if( s==t ) return 0;
 8     queue<string> q1,q2;
 9     q1.push(s); 
10     q2.push(t);
11     vis1.insert(s); 
12     vis2.insert(t);
13     int step = 0;
14     while( q1.size()&&q2.size() ){
15         if( q1.size() > q2.size() ){
16             swap(q1,q2);
17             swap(vis1,vis2);
18         }
19         step++;
20         int sz = q1.size();
21         while(sz--){
22             string f = q1.front(); q1.pop();
23             int c = f.find('0');
24             for(int i = 0; i < 4; i++){
25                 int t = d[i] + c;
26                 string fs = f;
27                 if(t < 0 or t > 8) continue;
28                 else if( i==0 && c%3==2  ) continue; 
29                 else if( i==1 && c%3==0  ) continue;
30                 swap(fs[t], fs[c]);
31                 if( vis2.count(fs) ) return step;
32                 if( vis1.count(fs) ) continue;
33                 q1.push(fs);
34                 vis1.insert(fs);
35             }            
36         }
37         step++;
38         sz = q2.size();
39         while(sz--){
40             string f = q2.front(); q2.pop();
41             int c = f.find('0');
42             for(int i = 0; i < 4; i++){
43                 int t = d[i] + c;
44                 string fs = f;
45                 if(t < 0 or t > 8) continue;
46                 else if( i==0 && c%3==2  ) continue; 
47                 else if( i==1 && c%3==0  ) continue;
48                 swap(fs[t], fs[c]);
49                 if( vis1.count(fs) ) return step;
50                 if( vis2.count(fs) ) continue;
51                 q2.push(fs);
52                 vis2.insert(fs);
53             }            
54         }
55     } 
56     return -1;
57 }
58 int main(){
59     cin >> s;
60     cout << bfs() << endl;
61     return 0;
62 } 
View Code 双向bfs,双队列等层交替扩展法,运行时间:143ms
#include<bits/stdc++.h>
using namespace std;
string s, t = "123804765";
int d[4] = {1, -1, 3, -3};
unordered_set<string> vis1,vis2;
int bfs(){
    if( s==t ) return 0;
    queue<string> q1,q2;
    q1.push(s); 
    q2.push(t);
    vis1.insert(s); 
    vis2.insert(t);
    int step = 0;
    while( q1.size()&&q2.size() ){
        if( q1.size() > q2.size() ){
            swap(q1,q2);
            swap(vis1,vis2);
        }
        step++;
        int sz = q1.size();
        while(sz--){
            string f = q1.front(); q1.pop();
            int c = f.find('0');
            for(int i = 0; i < 4; i++){
                int t = d[i] + c;
                string fs = f;
                if(t < 0 or t > 8) continue;
                else if( i==0 && c%3==2  ) continue; 
                else if( i==1 && c%3==0  ) continue;
                swap(fs[t], fs[c]);
                if( vis2.count(fs) ) return step;
                if( vis1.count(fs) ) continue;
                q1.push(fs);
                vis1.insert(fs);
            }            
        }
    } 
    return -1;
}
int main(){
    cin >> s;
    cout << bfs() << endl;
    return 0;
} 
View Code 双向bfs,优先扩展小队列动态平衡法,运行时间:140ms

 

posted @ 2026-09-03 12:58  费先生  阅读(14)  评论(0)    收藏  举报