广度优先搜索(BFS)的一个常见应用是找出从根结点到目标结点的最短路径。队列非常适合用来处理标准的BFS。
以计算最短路径为例,BFS通常有以下步骤:
- 创建一个合适大小的bool数组或哈希表来记录节点是否被遍历过,初始时将所有所有位置置为false;
- 创建队列,注意队列的数据类型
- 确定起点,并将起点入队,将起点对应位置置为true,路径长度level=1
- 当队列不为空时并且没有到达终点时循环以下步骤
- 将当前队列长度记为count,出队所有count个元素
- 每出队一个节点,依次判断它周围的点是否符合条件
- 若符合则入队并将相应位置置为true
- level+1,返回第三步
第6步中的条件可能有几种:
- 数组边界条件,0<=x<length
- 是否被遍历过
- 是否是可行的路径
- 如果节点记录了路径长度,原路径是否已经短于现路径
第5步是如果必须严格按照层次遍历时必须执行的,count记录了本层的元素数量
注意第4步,并不是所有问题都有终点,没有终点时条件仅为队列不空
墙与门
本题就没有严格遵照以上的第四步,因为每个节点都已经记录了当前最短的路径长度,不需知道当前的层次数,只要比前一层大1就可以了。循环条件也没有终点。
本题队列中的数据是坐标,所以队列数据类型是List<int>
public class Solution {
public void WallsAndGates(int[][] rooms) {
List<List<int>> door=new List<List<int>>();
for(int i=0;i<rooms.Length;i++)
for(int j=0;j<rooms[0].Length;j++)
{
if(rooms[i][j]==0)
{
List<int> temp=new List<int>();
temp.Add(i);
temp.Add(j);
door.Add(temp);
}
}//得到所有门的坐标
bool[,] gone=new bool[rooms.Length,rooms[0].Length];
for(int a=0;a<door.Count;a++)//几个门循环几次
{
for(int i=0;i<rooms.Length;i++)
for(int j=0;j<rooms[0].Length;j++)
gone[i,j]=false;
Queue<List<int>> q=new Queue<List<int>>();
q.Enqueue(door[a]);gone[door[a][0],door[a][1]]=true;
while(q.Count!=0)
{
List<int> index=new List<int>();
index=q.Dequeue();
if(index[0]-1>=0&&!gone[index[0]-1,index[1]]&&rooms[index[0]-1][index[1]]!=-1
&&rooms[index[0]][index[1]]+1<rooms[index[0]-1][index[1]])
{
rooms[index[0]-1][index[1]]=rooms[index[0]][index[1]]+1;
List<int> temp=new List<int>();
temp.Add(index[0]-1);temp.Add(index[1]);
gone[index[0]-1,index[1]]=true;
q.Enqueue(temp);
}
if(index[0]+1<rooms.Length&&!gone[index[0]+1,index[1]]
&&rooms[index[0]+1][index[1]]!=-1
&&rooms[index[0]][index[1]]+1<rooms[index[0]+1][index[1]])
{
rooms[index[0]+1][index[1]]=rooms[index[0]][index[1]]+1;
List<int> temp=new List<int>();
temp.Add(index[0]+1);temp.Add(index[1]);
gone[index[0]+1,index[1]]=true;
q.Enqueue(temp);
}
if(index[1]-1>=0&&!gone[index[0],index[1]-1]
&&rooms[index[0]][index[1]-1]!=-1
&&rooms[index[0]][index[1]]+1<rooms[index[0]][index[1]-1])
{
rooms[index[0]][index[1]-1]=rooms[index[0]][index[1]]+1;
List<int> temp=new List<int>();
temp.Add(index[0]);temp.Add(index[1]-1);
gone[index[0],index[1]-1]=true;
q.Enqueue(temp);
}
if(index[1]+1<rooms[0].Length&&!gone[index[0],index[1]+1]
&&rooms[index[0]][index[1]+1]!=-1
&&rooms[index[0]][index[1]]+1<rooms[index[0]][index[1]+1])
{
rooms[index[0]][index[1]+1]=rooms[index[0]][index[1]]+1;
List<int> temp=new List<int>();
temp.Add(index[0]);temp.Add(index[1]+1);
gone[index[0],index[1]+1]=true;
q.Enqueue(temp);
}
}
}
return;
}
}
打开转盘锁
这道题实际上就是一个四维图中的最短路径,处理bool数组时把字符串转化为数字比较方便。
C#中字符+数字得到的相应ASCII码值,如'0'+1=49,再进行强制转换可以得到相应字符,
如(char)('0'+1)='1'
本题队列中的数据是字符串,不过为了处理数据的方便,所以队列数据类型是List<string>
public class Solution {
public int OpenLock(string[] deadends, string target) {
Queue<List<string>> q=new Queue<List<string>>();
int num=0;bool arrive=false;
bool[] gone=new bool[10000];
for(int i=0;i<10000;i++)
gone[i]=false;
string start="0000";
if(indead(deadends,start))
return -1;
List<string> s=new List<string>();
s.Add(start);q.Enqueue(s);
while(q.Count!=0&&!arrive)
{
num++;int count=q.Count;
for(int i=0;i<count;i++)
{
List<string> temp=new List<string>();
temp=q.Dequeue();
for(int j=0;j<temp.Count;j++)
{
if(temp[j]!=target)
q.Enqueue(rotate(deadends,temp[j],gone));
else
{arrive=true;break;}
}
if(arrive)
break;
}
}
if(arrive)
return num-1;
else
return -1;
}
public List<string> rotate(string[] deadends,string initial,bool[] gone)
{
List<string> res=new List<string>();
for(int i=0;i<4;i++)
{
string temp="";
for(int j=0;j<4;j++)
{
if(j!=i)
temp+=initial[j];
else
{
if(initial[i]<'9')
temp+=(char)(initial[i]+1);
else
temp+='0';
}
}
if(!gone[Convert.ToInt32(temp)]&&!indead(deadends,temp))
{
res.Add(temp);
gone[Convert.ToInt32(temp)]=true;
}
temp="";
for(int j=0;j<4;j++)
{
if(j!=i)
temp+=initial[j];
else
{
if(initial[i]>'0')
temp+=(char)(initial[i]-1);
else
temp+='9';
}
}
if(!gone[Convert.ToInt32(temp)]&&!indead(deadends,temp))
{
res.Add(temp);
gone[Convert.ToInt32(temp)]=true;
}
}
return res;
}
public bool indead(string[] deadends,string str)
{
for(int i=0;i<deadends.Length;i++)
if(deadends[i]==str)
return true;
return false;
}
}
01 矩阵
给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。两个相邻元素间的距离为 1 。
首先确定起点,看起来0是不用改变,似乎可以从1开始,但是1还分为与0相邻和不相邻两种,其中实际可能有多层,所以起点应该是0结点。
队列中的数据是坐标,所以队列数据类型为List<int>
因为路径长度严格等于层次数,所以,每次出队必须出队所有元素。
public class Solution {
public int[][] UpdateMatrix(int[][] mat) {
bool[,] gone=new bool[mat.Length,mat[0].Length];
Queue<List<int>> q=new Queue<List<int>>();
for(int i=0;i<mat.Length;i++)
for(int j=0;j<mat[0].Length;j++)
{
if(mat[i][j]==0)
{
List<int> index=new List<int>();
index.Add(i);index.Add(j);
q.Enqueue(index);
gone[i,j]=true;
}
else
gone[i,j]=false;
}
int level=1;
while(q.Count!=0)
{
int count=q.Count;
for(int i=0;i<count;i++)
{
List<int> index=new List<int>();
index=q.Dequeue();
if(index[0]-1>=0&&!gone[index[0]-1,index[1]])
{
List<int> temp=new List<int>();
temp.Add(index[0]-1);temp.Add(index[1]);
gone[temp[0],temp[1]]=true;
mat[temp[0]][temp[1]]=level;
q.Enqueue(temp);
}
if(index[0]+1<mat.Length&&!gone[index[0]+1,index[1]])
{
List<int> temp=new List<int>();
temp.Add(index[0]+1);temp.Add(index[1]);
gone[temp[0],temp[1]]=true;
mat[temp[0]][temp[1]]=level;
q.Enqueue(temp);
}
if(index[1]-1>=0&&!gone[index[0],index[1]-1])
{
List<int> temp=new List<int>();
temp.Add(index[0]);temp.Add(index[1]-1);
gone[temp[0],temp[1]]=true;
mat[temp[0]][temp[1]]=level;
q.Enqueue(temp);
}
if(index[1]+1<mat[0].Length&&!gone[index[0],index[1]+1])
{
List<int> temp=new List<int>();
temp.Add(index[0]);temp.Add(index[1]+1);
gone[temp[0],temp[1]]=true;
mat[temp[0]][temp[1]]=level;
q.Enqueue(temp);
}
}
level++;
}
return mat;
}
}