广度优先搜索(BFS)算法,概念就不说啥了,常用来求最短路径,最少步数等,比如求二叉树最小高度。
BFS与DFS:简单理解就是DFS找会先沿着一条路径一直往深处走走到头在往回退重重这个过程。BFS则是沿着多个可选路径齐头并进,每次只向前走一小步,最终哪条路径先走到终点则走的步数就是最短路径。BFS先空间复杂度相比于DFS较高,时间复杂度最坏为O(N).
总体解体框架参考labuladong的算法小抄中的结构,如下图所示:
核心数据结构为一个queue,需要用这个queue记录每一层次的数据节点,还有一个visited记录已经访问过的数据(使用set或者其他结构来),还有一个step来控制扩散的层数(步数)。具体做题感受吧。
这里记录了几个经典例题:
https://leetcode.cn/problems/employee-importance/
这个题可以用bfs来做,员工信息用map关联起来方便查询。
class Solution {
public:
int getImportance(vector<Employee*> employees, int id) {
if(employees.empty()) return 0;
for(auto e:employees)
mp1[e->id]=e;
BFS
queue<int> q1;
q1.push(id);
while(!q1.empty()){
sum += mp1[q1.front()]->importance;
for(auto e: mp1[q1.front()]->subordinates)
{
q1.push(e);
}
q1.pop();
}
return sum;
}
private:
int sum=0;
unordered_map<int,Employee*> mp1;
};
https://leetcode.cn/problems/rotting-oranges/
类似于图像渲染,一层一层向外扩散,很明显要BFS来求最短步数。
这里我用了Node结构体来封装x,y坐标,方便访问,也可以使用pair
这个题刚开始练手做的,感觉代码写的不是很好。
class Solution {
public:
struct Node{
Node(int _x,int _y):x(_x),y(_y)
{}
int x;
int y;
};
int orangesRotting(vector<vector<int>>& grid) {
int row=grid.size();
int col=grid[0].size();
if(grid.empty()) return -1;
vector<vector<bool>> visited(row,vector<bool>(col,false));
queue<Node> q1;
int suma=0,sumb=0;//suma 为橘子数,sumb为入队的橘子数
for(int i=0;i<row;++i){ //初始节点入队
for(int j=0;j<col;++j){
if(grid[i][j]!=0) suma++;
if(grid[i][j]==2){ //可能有多个腐烂的橘子
q1.push(Node(i,j));
visited[i][j]=true;
}
}
}
int deep=0;
while(!q1.empty()){
deep++;
int size=q1.size();
while(size--){
Node node1=q1.front();
q1.pop();
sumb++;
for(int i=0;i<4;++i){ //坐标扩散
int x =node1.x + nextposition[i][0];
int y =node1.y + nextposition[i][1];
if(x>=0 && x<row && y>=0 && y<col
&&grid[x][y]==1
&& visited[x][y]==false){
q1.push(Node(x,y));
visited[x][y]=true;
//grid[x][y]=2;
}
}
}
}
if(suma==0) return 0;
return suma==sumb? deep-1:-1;
}
private:
int nextposition[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
};
https://leetcode.cn/problems/n-ary-tree-level-order-traversal/
/*
// Definition for a Node.
class Node {
public:
int val;
vector children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
vector<vector<int>> retv;
if(!root) return retv;
queue<Node*> q1;
q1.push(root);
while(!q1.empty()){
vector<int> v1;
int size=q1.size();
while(size--){
Node* n1=q1.front();
q1.pop();
for(auto e : n1->children){
q1.push(e);
}
v1.push_back(n1->val);
}
retv.push_back(v1);
v1.clear();
}
return retv;
}
};
https://leetcode.cn/problems/word-ladder/
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
//hash表的查询效率最高
unordered_set<string> wordDict(wordList.begin(), wordList.end());
//标记单词是否已经访问过,访问过的不再访问
unordered_set<string> visited;
visited.insert(beginWord);
//初始化队列
queue<string> q;
q.push(beginWord);
int res = 1;
while (!q.empty()){
int nextSize = q.size();
//每一步都要把队列中上一步添加的所有单词转换一遍
//最短的转换肯定在这些单词当中, 所有这些词的转换只能算一次转换
//因为都是上一步转换出来的
while (nextSize--){
string curWord = q.front();
q.pop();
//尝试转换当前单词的每一个位置
for (int i = 0; i < curWord.size(); i++){
string newWord = curWord;
//每一个位置用26个字母分别替换
for (auto ch = 'a'; ch <= 'z'; ch++){
newWord[i] = ch;
//如果列表中没有此单词或者已经访问过(它的转换已经遍历过,无需再次遍历),则跳过
if (!wordDict.count(newWord) || visited.count(newWord))
continue;
//转换成功,则在上一步转换的基础上+1
if (newWord == endWord)
return res + 1;
//还没有转换成功,则新的单词入队
visited.insert(newWord);
q.push(newWord);
}
}
}
res++;
}
//转换不成功,返回0
return 0;
}
https://leetcode.cn/problems/minimum-genetic-mutation/
class Solution {
public:
int minMutation(string start, string end, vector<string>& bank) {
unordered_set<string> bankdict(bank.begin(), bank.end()); //hash查找效率高
queue<string> q1;
unordered_set<string> visited;
q1.push(start);
visited.insert(start);
int step=0;
while(!q1.empty()){
int size = q1.size();
for(int i=0;i<size;++i){ //节点扩散
string cur = q1.front();
q1.pop();
if(cur==end) //终点判断
return step;
for(int i=0;i<cur.size();++i){ //新节点入队
string tmp = cur;
for(int k=0;k<4;++k){
tmp[i]=ch[k];
if(bankdict.count(tmp) && !visited.count(tmp)){//满足条件入队,并标记
q1.push(tmp);
visited.insert(tmp);
}
}
}
}
step++;
}
return -1;
}
private:
char ch[4]={'A','C','T','G'};
};
https://leetcode.cn/problems/open-the-lock/
class Solution {
public:
int openLock(vector<string>& deadends, string target) {
unordered_set<string> deadendcict(deadends.begin(),deadends.end()); //hash查找效率高
queue<string> q1;
q1.push(string("0000"));
unordered_set<string> visited;
visited.insert(q1.front());
//特殊情况
if(deadendcict.count("0000") || deadendcict.count(target)) return -1;
int step=0;
while(!q1.empty()){
int size=q1.size();
while(size--){
string cur=q1.front();
q1.pop();
for(int i=0;i<cur.size();++i){//遍历下一步可能的选择
if(strcmp(target.c_str(),cur.c_str())==0) //终点判断
return step;
for(int k=0;k<3;k=k+2){
string tmp=cur;
tmp[i]=ch[tmp[i]-'0'+k]; //+1或者-1
if( !deadendcict.count(tmp) && !visited.count(tmp)){ //死锁表里没有且没访问过
visited.insert(tmp);
q1.push(tmp);
}
}
}
}
step++;
}
return -1;
}
private:
char ch[12]={'9','0','1','2','3','4','5','6','7','8','9','0'};
};
4 5 6这三个题一模一样的套路。