廣度優先搜索算法 - BFS


廣度優先搜索(breadth-first search -- BFS)

廣度優先搜索又叫做 寬度優先搜索,其英文縮寫為BFS,是我們在解決圖類問題和樹上問題的一個很好的解決算法。

BFS通常幫助我們解決一類最優問題: 距離最短,次數最少,時間最短等...以及連通塊等圖問題

如果你前面認真學習了深度優先搜索的話,你會發現深度優先搜索的特性是搜索出每一個可能結果將所有結果匯總進行比較最終得出最優解。這樣對於常數很小的問題 深度優先搜索可以起到很好的作用,但是如果對於常數很大的問題,我們會發現此時我們在使用dfs會出現超時的情況(TLE), 這個時候就需要引入我們的廣度優先搜索了,廣度優先搜索可以有效的解決此類求解最優解問題。


在我們學習廣度優先搜索之前我們需要先學習一種數據結構: 隊列

隊列(queue)

隊列的基本概念
隊列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的后端(rear)進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。隊列中沒有元素時,稱為空隊列。
隊列的數據元素又稱為隊列元素。在隊列中插入一個隊列元素稱為入隊,從隊列中刪除一個隊列元素稱為出隊。因為隊列只允許在一端插入,在另一端刪除,所以只有最早進入隊列的元素才能最先從隊列中刪除,故隊列又稱為先進先出(FIFO—first in first out)(先來先服務)線性表。

大家可以把隊列這種數據結構想像稱為我們在食堂排隊打飯 hh,排在前面的優先打上飯,而排在后面要等前面人都打上飯才能去打飯


上面這個過程就體現了隊列的出隊過程

如果你來晚了也想去吃飯,那么你只能從隊列的尾巴開始排隊,這就是隊列的入隊過程


大家暫時可以不用在意這些數據結構是怎么實現的,因為這部分內容之后上數據結構課程時會學到,我們現在只需要知道隊列在C++ / Java語言中怎么使用即可

C++ 中的隊列使用

#include <iostream>
#include <queue>// 引入隊列頭文件

using namespace std;

int main()
{
	// 創建一個隊列 指定類型
	queue<int> q;
	
	// 往隊列中添加元素
	
	q.push(1);
	q.push(2);
	q.push(3);
	
	// 出隊
	while(!q.empty())
	{
		// 取出隊首 
		int first = q.front();
		cout << first << " ";
		q.pop(); 
	} 
	return 0;	
} 

// 打印結果
1 2 3

Java中隊列的使用

import java.util.LinkedList;
import java.util.Queue;

public class queue {
    public static void main(String[] args) {
        創建隊列
        Queue<Integer> q = new LinkedList<>();
        往隊列中添加元素
        q.offer(1);
        q.offer(2);
        q.offer(3);

        依次打印棧的元素
        while(!q.isEmpty())
        {
            int first = q.poll();
            System.out.println(first);
        }
    }
}

現在已經知道了隊列這種數據結構下面我們學習如何使用隊列來進行廣度優先搜索

下面我們根據一道例題來學習廣度優先搜索的使用

題目傳送門

題目描述

有一個n*m的棋盤(1<n,m<=400),在某個點上有一個馬,要求你計算出馬到達棋盤上任意一個點最少要走幾步
輸入格式

一行四個數據,棋盤的大小和馬的坐標
輸出格式

一個n*m的矩陣,代表馬到達某個點最少要走幾步(左對齊,寬5格,不能到達則輸出-1)

輸入輸出樣例

輸入 #1


3 3 1 1

輸出 #1

0    3    2    
3    -1   1    
2    1    4    

分析:
首先我們讀題發現題目讓我們求得是最少步數,那么我們優先使用廣度優先搜索(在這里對比一下兩種搜索)。

根據樣例信息我們可以得到如下所示圖:

題目中說得是馬得行走方式,那么馬走日這個規矩相信我就不用多說了吧,馬走一步可以到達圖上哪些點呢?

圖中綠色得地方就代表馬可以跳的得地方。怎么樣才能夠得出這些坐標呢 大家自己仔細想一下。
我們已經知道了馬一次可以跳到得點那么我們可以根據跳到得位置在來到下一次能跳得位置,然后不斷重復這個過程就可以得到每一個可以走過得位置。
而且我們會發現我們第一次走過得位置就是到達該位置得最小步數,因為我們一次只走了一步。詳情聽視頻分解

詳細得廣搜過程帶着大家寫一遍代碼體會,注意看視頻。
代碼過程

#include <iostream>
#include <algorithm>
#include <cstdio> 
#include <cmath>
#include <queue>
using namespace std;
int n, m;
int  map[401][401];
bool vis[401][401];
int _next[8][2] = { { -2, -1 }, { -2, 1 }, { -1, -2 }, { -1, 2 },  { 1,   2 }, { 1, -2 }, { 2,   1 }, { 2, -1 } };;
typedef struct node {
	int x, y;
}N;
bool inmap(N a)
{
	return (a.x >= 1 && a.x <= n && a.y >= 1 && a.y <= m);
}
void outans()
{
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			printf("%-5d", map[i][j]);
		}
		cout << endl;
	}
}
void bfs(N start)
{
	queue<N> q;
	map[start.x][start.y] = 0;
	vis[start.x][start.y] = 1;
	q.push(start);
	while (!q.empty())
	{
		N now = q.front();
		for (int i = 0; i < 8; i++)
		{
			N tmp;
			tmp.x = _next[i][0] + now.x;
			tmp.y = _next[i][1] + now.y;
			if (inmap(tmp) == true && vis[tmp.x][tmp.y] == 0)
			{
				vis[tmp.x][tmp.y] = 1;
				map[tmp.x][tmp.y] = map[now.x][now.y] + 1;
				q.push(tmp);
			}
		}
		q.pop();
	}
	outans();
}
void init()
{
	N s;
	fill(map[0], map[0]+ 401*401, -1);
	cin >> n >> m >> s.x >> s.y;
	bfs(s);
}
int main()
{
	init();
	system("pause");
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM