广度优先搜索(Breadth-First-Search)
广度优先搜索(BFS):是一种遍历或搜索数据结构(如树或图)的算法。
广度优先搜索与队列
用示例来解释:
Q:A-G 的最短路径?
在遍历当前节点时,将其子节点添入队列
Round1: A -> B,C,D //遍历结点A,向队列中添加结点B,C,D
Round2:B,C,D ->E,F,G
Round3:E,F,G ->G(visited) //本该添加F的子结点G,但G在上一轮中已添加过
BFS是一层层进行的,遍历数的结点。彻底地搜索整张图,直到找到结果
实例
leetcode 200.岛屿数量
要素
(1)为岛屿的判断标准: 陆地(1)上下左右是否被水(0)包围
(2)岛屿的数量 == 整个联通陆地(1)的数量
采取BFS解决思路
key:从初始岛屿出发向上下左右元素访问,被访问元素中若A为岛屿(1),再访问A的上下左右元素。重复以上
step1:找到为(1)的初始岛屿,先将其入队,再利用FIFO性质取出该元素坐标
step2:对该元素坐标的上下左右进行访问,对被访问元素e进行分类讨论
1)e未被访问,将其坐标入队等待访问
2)e已被访问,跳过
重复step1,2直至队列为空,说明元素周围没有陆地,便可看作岛屿。岛屿的数量就等于队列为空的遍历次数。
AC代码如下
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
//空输入
if(grid.empty())
return 0;
//行列初始化
int row = grid.size();
int col = grid[0].size();
//定义方向
int dx[4] = {-1,0,1,0};//行方向
int dy[4] = {0,1,0,-1};//列方向
//定义岛屿数量
int cnt = 0;
//定义访问记录
vector<vector<bool>> visited(row, vector<bool>(col,false));//初始都为未访问
queue<pair<int, int>>que;// 定义队列,队列每个元素存放的是一个坐标
//main part
for(int i = 0; i < row; i++){
for(int j = 0; j < col; j++){//遍历直至出现初始陆地
if(grid[i][j] == '1' && visited[i][j] == false)//找到未被访问的初始陆地
{
que.push(make_pair(i,j));
cnt++;
visited[i][j] = true;//标记访问
while(!que.empty()){
int x = que.front().first;//获得行坐标
int y = que.front().second;//获得列坐标
que.pop();//获得坐标后出队
for(int k = 0; k < 4; k++){//获得上下左右的信息
int xx = x + dx[k];
int yy = y + dy[k];
//先满足最基本条件:在grid区域内
if(xx >=0 && xx < row && yy >=0 && yy < col){
//若该元素未被访问且是陆地
if(visited[xx][yy] == false && grid[xx][yy] == '1'){
visited[xx][yy] = true;//标记访问
que.push(make_pair(xx,yy));//入队,等待访问
}
}
}
}
}
}
}
return cnt;
}
};
leetcode 279. 完全平方数
要素
(1)重复n= n-i*i直至n=0,i每次从1开始增大
(2)完全平方数之和最少 == n —>0 路径最短
采取BFS解决思路
step1:创建square数组(代码中是从大到小的顺序以减少时间)
step2:对square的元素遍历,对num-i的结果分类讨论
1)num-i == 0,找到最短路径,当前树的高度==完全平方数最少数量
2)num-i > 0,未找到最短路径,将树延伸下去,将下一层树的一个叶子存入队列,等待下一次的判断
AC代码如下
class Solution {
public:
int numSquares(int n) {
//step1 创建square数组
vector<int> square;
for(int i = 1; i*i <= n; i++)//
square.insert(square.begin(), i*i);//从大到小存入i*i
queue<pair<int,int>> que;//分别对应当前num值和树的高度
que.push(make_pair(n, 1));//树的高度至少为1
while(1){
int num = que.front().first;//取出num
int len = que.front().second;//取出树的高度
que.pop();//出队
for(auto i : square){//遍历数组square中的值
if(i == num)//找到最短路径
return len; //返回当前树的高度
if(i < num)//继续延伸树,再向下找
que.push(make_pair(num-i, len+1));//进行树的延伸,树的高度加1
}
}
}
};
PS:
for(int i = 1; i*i <= n; i++)
square.insert(square.begin(), i*i);
ps 关于insert用法
#include <bits/stdc++.h>
using namespace std;
int main()
{
string str("All that exists is what's ahead.");
string a, b;
a = str.insert(4,"sky");
//在下标为4的位置,插入字符串sky
cout << a << endl; //输出All skythat exists is what's ahead.
str = "All that exists is what's ahead.";
b = str.insert(4,5,'x');
//在下标为4的位置,插入字符串5个字符x
cout << b << endl; //输出 All xxxxxthat exists is what's ahead.
return 0;
}
leetcode 752.打开转盘锁
要素
注释:s:结点数字 ; deadends:死亡数字
(1)s不能存在deadends包含数字
(2)每次只能变化一个数字且只能变化一位
采取BFS解决思路
将初始字符0000每一位数字+1/-1,得到8个结果,将8个结果的每一位数字分别再进行上述操作。可发现是树
对于操作后得到的数字结果:
i) 与之前的结果重复 ->跳过(否则出现死循环)
ii)包含在deadends内 ->跳过(题意要求)
iii)得到新的一串数字 ->继续每位数字+1/-1的操作(延伸树)
用visited来跳过i)和ii)的结果,这里涉及新知识点map(可以更快地检索)
AC代码如下
class Solution {
public:
int openLock(vector<string>& deadends, string target) {
unordered_map<string,int> visited;
for(auto i : deadends)//将deadends中的数字存入visited。标记为已访问
visited[i] = 1;
if(visited["0000"] == 1)//若初始数字在deadends中,则返回-1
return -1;
queue<string> que;
int cnt = 0;
que.push("0000");
while(!que.empty()){
int n = que.size();//获得这层树的叶数,以对每个结点进行操作
while(n--){//遍历该层所有结点
string s = que.front();
que.pop();
if(s == target)//若此时有结点==target,返回树的高度
return cnt;
if(visited[s] != 1){//必须满足未访问
visited[s] = 1;//标记访问
for(int i = 0; i < 4; i++){
string s_1 = s;//进行+1操作的字符串
string s_2 = s;//进行-1操作的字符串
s_1[i] = s_1[i] == '9' ? '0' : s_1[i]+1;//若该数字为9则+1变为0
s_2[i] = s_2[i] == '0' ? '9' : s_2[i]-1;//若该数字为0则-1变9
//对未访问的数字加入队列等待访问
if(visited[s_1] != 1)
que.push(s_1);
if(visited[s_2] != 1)
que.push(s_2);
}
}
}
cnt++;
}
return -1;
}
};