广度优先搜索(Breadth First Search) 学习
Posted 楠c
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了广度优先搜索(Breadth First Search) 学习相关的知识,希望对你有一定的参考价值。
借助队列其先进先出的特点,将要搜索的放在队尾,他是一种地毯式搜索,所以第一次找到目标,一定是最短路径
员工重要性
/*
// Definition for Employee.
class Employee {
public:
int id;
int importance;
vector<int> subordinates;
};
*/
class Solution {
public:
int getImportance(vector<Employee*> employees, int id) {
unordered_map<int,Employee*> map;
for(auto& e:employees)
{
map[e->id]=e;
}
queue<int> q;
q.push(id);
int ret=0;
while(!q.empty())
{
int top=q.front();
q.pop();
ret+=map[top]->importance;
for(auto& e:map[top]->subordinates)
{
q.push(e);
}
}
return ret;
}
};
N叉树的层序遍历
和二叉树层序遍历思路一样,只不过他这里的结构体里用数组存储了他的孩子。将孩子一次入队即可。
/*
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector<Node*> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
vector<vector<int>> vv;
if(root==NULL)
return vv;
queue<Node*> q;
q.push(root);
while(!q.empty())
{
int size=q.size();
vector<int> temp;
while(size--)
{
Node* Tnode=q.front();
q.pop();
temp.push_back(Tnode->val);
for(auto& e:Tnode->children)
{
if(e)
q.push(e);
}
}
vv.push_back(temp);
}
return vv;
}
};
腐烂的橘子
这里需要对四周同时污染,所以定义一个二维数组,表示其搜索范围。
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int next[4][2]={{1,0},
{-1,0},
{0,1},
{0,-1}};
int m=grid.size();
if(m==0)
return 0;
int n=grid[0].size();
queue<pair<int,int>> q;
//找到腐烂的橘子入队
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(grid[i][j]==2)
{
q.push(make_pair(i,j));
}
}
}
int count=0;
while(!q.empty())
{
int flag=0;
//处理当前队列中腐烂的橘子,因为要算出分钟,所以必须用size来保存每一次队列中腐烂的橘子
//因为这一批腐烂的橘子是同时污染的。
int size=q.size();
while(size--)
{
pair<int,int> top=q.front();
q.pop();
//把它的周围变成腐烂的橘子
for(int i=0;i<4;i++)
{
int nx=top.first+next[i][0];
int ny=top.second+next[i][1];
if(nx<0||ny<0||nx>=m||ny>=n||grid[nx][ny]!=1)
{
continue;
}
flag=1;
grid[nx][ny]=2;
//变成腐烂橘子之后入队,腐烂他周围新鲜橘子
q.push(make_pair(nx,ny));
}
}
//队列中当前一批腐烂橘子处理完才++一次
if(flag)
count++;
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(grid[i][j]==1)
{
return -1;
}
}
}
return count;
}
};
单词接龙
将所给的单词放入哈希表中作为字典。
需要用标记字典,单词有可能重复。
他们两都为哈希set,查询效率高。
- 先入单词到队列中,同时对其做标记
- 取出这个单词,用26个字母对齐拼接
- 必须是,在字典中且没有被标记过才能入队列,用26个字母试探
- 处理完一批在++,因为有可能有一批单词转换
代表样例便是hit->hot。hot可以转换为dot和lot,但是dot转换步数最小。
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
//字典
unordered_set<string> dict;
for(auto& e:wordList)
{
dict.insert(e);
}
//标记
unordered_set<string> vist;
vist.insert(beginWord);
queue<string> q;
q.push(beginWord);
int count=1;
while(!q.empty())
{
int size=q.size();
//对队列中的一批单词进行转换
while(size--)
{
string Tstr=q.front();
q.pop();
if(Tstr==endWord)
return count;
for(int i=0;i<Tstr.size();i++)
{
//写到这里是因为每次对第一位26次试探结束,重新变回原样对第二位进行试探
//例如第一次 hit从 ait,bit,cit...一直试探,
string Nstr=Tstr;
for(char j='a';j<='z';j++)
{
Nstr[i]=j;
//存在字典中,且没有被遍历过
if(dict.count(Nstr)&&!vist.count(Nstr))
{
//入队
q.push(Nstr);
//然后打上标记
vist.insert(Nstr);
}
}
}
}
count++;
}
return 0;
}
};
最小基因变化
和单词接龙类似,bank为词典,vis我们定义的标记词典。拼接序列是它给定的ACGT
class Solution {
public:
int minMutation(string start, string end, vector<string>& bank) {
//词典
unordered_set<string> dict;
for(auto& e:bank)
{
dict.insert(e);
}
//标记
unordered_set<string> vist;
vist.insert(start);
//队列
queue<string> q;
q.push(start);
int count=0;
string str="ACGT";
while(!q.empty())
{
int size=q.size();
while(size--)
{
string Tstr=q.front();
q.pop();
if(Tstr==end)
return count;
for(int i=0;i<start.size();i++)
{
string Nstr=Tstr;
for(char j=0;j<str.size();j++)
{
Nstr[i]=str[j];
//词典包含基因序列,且没被遍历过过才入队
if(dict.count(Nstr)&&!vist.count(Nstr))
{
q.push(Nstr);
vist.insert(Nstr);
}
}
}
}
count++;
}
return -1;
}
};
打开转盘锁
字典为死亡字典,之前的逻辑是在字典中且未被标记过才能入队,根据这里的题干要求,等会的逻辑需要变成不在死亡字典,且未被标记过才能入队。
之前的单词接龙和基因变化,需要拼接单词,但是这里的转盘锁,直接在锁的基础上对每一位进行变化,而且还有两种方式,所以需要定义两个临时字符串,一个+,一个-。当9继续++时需要变成0,当0再继续–时需要变成9。
class Solution {
public:
int openLock(vector<string>& deadends, string target) {
//字典
unordered_set<string> dict(deadends.begin(),deadends.end());
//丧心病狂的用例,起始位置直接死亡
if(dict.count("0000"))
return -1;
queue<string> q;
q.push("0000");
//标记字典以防重复
unordered_set<string> vis;
vis.insert("0000");
int step=0;
while(!q.empty())
{
int size=q.size();
while(size--)
{
string Tstr=q.front();
q.pop();
if(Tstr==target)
{
return step;
}
for(int i=0;i<Tstr.size();i++)
{
//因为它有两种变化方式
string str1=Tstr;
string str2=Tstr;
//等于0,--变成9,否则每次减1
str1[i]=str1[i]=='0'?'9':str1[i]-1;
//等于9,++变成0,否则每次加1
str2[i]=str2[i]=='9'?'0':str2[i]+1;
//不在死亡字典,且没有被遍历过
if(!dict.count(str1) && !vis.count(str1))
{
q.push(str1);
vis.insert(str1);
}
if(!dict.count(str2) && !vis.count(str2))
{
q.push(str2);
vis.insert(str2);
}
}
}
step++;
}
return -1;
}
};
以上是关于广度优先搜索(Breadth First Search) 学习的主要内容,如果未能解决你的问题,请参考以下文章
CS 188 Breadth First Search BFS(广度优先搜索算法)
广度优先搜索(Breadth First Search) 学习
广度优先搜索(Breadth First Search) 学习