深度优先搜索和广度优先搜索广泛运用于树和图中,但是它们的应用远远不止如此。
BFS
广度优先搜索一层一层地进行遍历,每层遍历都以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点。需要注意的是,遍历过的节点不能再次被遍历。
第一层:
- 0 -> {6,2,1,5}
第二层:
- 6 -> {4}
- 2 -> {}
- 1 -> {}
- 5 -> {3}
第三层:
- 4 -> {}
- 3 -> {}
每一层遍历的节点都与根节点距离相同。设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。利用这个结论,可以求解最短路径等 最优解 问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径,无权图是指从一个节点到另一个节点的代价都记为 1。
在程序实现 BFS 时需要考虑以下问题:
- 队列:用来存储每一轮遍历得到的节点;
- 标记:对于遍历过的节点,应该将它标记,防止重复遍历。
1. 计算在网格中从原点到特定点的最短路径长度
1091. Shortest Path in Binary Matrix(Medium)
[[1,1,0,1],
[1,0,1,0],
[1,1,1,1],
[1,0,1,1]]
题目描述:0 表示可以经过某个位置,求解从左上角到右下角的最短路径长度。
class Solution {
public:
vector<vector<int>>dir={
{
0,1},{
0,-1},{
1,0},{
-1,0},{
1,1},{
1,-1},{
-1,1},{
-1,-1}};
int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
if(grid[0][0]==1)return -1;
int n=grid.size();
queue<pair<int,int>>q;
q.push(make_pair(0,0));
int length=1;
grid[0][0]=2; //将访问过的点标记为2
while(!q.empty()){
int l=q.size(); //遍历当前队列所有的元素
for(int i=0;i<l;i++){
int x=q.front().first;
int y=q.front().second;
q.pop();
if(x==n-1&&y==n-1)return length;
for(int j=0;j<8;j++){
int x1=x+dir[j][0];
int y1=y+dir[j][1];
if(x1<0||y1<0||x1>=n||y1>=n||grid[x1][y1])continue; //越界或者不满足访问条件跳过
grid[x1][y1]=2;
q.push(make_pair(x1,y1));
}
}
length++;
}
return -1;
}
};
2. 组成整数的最小平方数数量
279. Perfect Squares (Medium)
For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.
可以将每个整数看成图中的一个节点,如果两个整数之差为一个平方数,那么这两个整数所在的节点就有一条边。
要求解最小的平方数数量,就是求解从节点 n 到节点 0 的最短路径。
本题也可以用动态规划求解,在之后动态规划部分中会再次出现。
class Solution {
public:
int numSquares(int n) {
vector<int> dp(1,0);
while(dp.size()<=n){
int m = dp.size(), val = m;
for(int i=1; i*i<=m; i++){
val = min(val, dp[m-i*i]+1);
}
dp.push_back(val);
}
return dp.back();
}
};
3. 最短单词路径
127. Word Ladder (Medium)
Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
Output: 5
Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
Output: 0
Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
题目描述:找出一条从 beginWord 到 endWord 的最短路径,每次移动规定为改变一个字符,并且改变之后的字符串必须在 wordList 中。
class Solution{
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList){
//加入所有节点,访问过一次,删除一个。
unordered_set<string> s;
for (auto &i : wordList) s.insert(i);
queue<pair<string, int>> q;
//加入beginword
q.push({
beginWord, 1});
string tmp; //每个节点的字符
int step; //抵达该节点的step
while ( !q.empty() ){
if ( q.front().first == endWord){
return (q.front().second);
}
tmp = q.front().first;
step = q.front().second;
q.pop();
//寻找下一个单词了
char ch;
for (int i = 0; i < tmp.length(); i++){
ch = tmp[i];
for (char c = 'a'; c <= 'z'; c++){
//从'a'-'z'尝试一次
if ( ch == c) continue;
tmp[i] = c ;
//如果找到的到
if ( s.find(tmp) != s.end() ){
q.push({
tmp, step+1});
s.erase(tmp) ; //删除该节点
}
tmp[i] = ch; //复原
}
}
}
return 0;
}
};
DFS
![](https://i-blog.csdnimg.cn/blog_migrate/9a223ceff8d03e3c56d723862510b6ad.png)
广度优先搜索一层一层遍历,每一层得到的所有新节点,要用队列存储起来以备下一层遍历的时候再遍历。
而深度优先搜索在得到一个新节点时立即对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。
从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 可达性 问题。
在程序实现 DFS 时需要考虑以下问题:
- 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。
- 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。
1. 查找最大的连通面积
695. Max Area of Island (Medium)
[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
class Solution {
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
int ans = 0;
for (int i = 0; i != grid.size(); ++i)
for (int j = 0; j != grid[0].size(); ++j) {
int cur = 0;
stack<int> stacki;
stack<int> stackj;
stacki.push(i);
stackj.push(j);
while (!stacki.empty()) {
int cur_i = stacki.top(), cur_j = stackj.top();
stacki.pop();
stackj.pop();
if (cur_i < 0 || cur_j < 0 || cur_i == grid.size() || cur_j == grid[0].size() || grid[cur_i][cur_j] != 1)
continue;
++cur;
grid[cur_i][cur_j] = 0;
int di[4] = {
0, 0, 1, -1};
int dj[4] = {
1, -1, 0, 0};
for (int index = 0; index != 4; ++index) {
int next_i = cur_i + di[index], next_j = cur_j + dj[index];
stacki.push(next_i);
stackj.push(next_j);
}
}
ans = max(ans, cur);
}
return ans;
}
};
2. 矩阵中的连通分量数目
200. Number of Islands (Medium)
Input:
11000
11000
00100
00011
Output: 3
可以将矩阵表示看成一张有向图。
class Solution {
public:
int dx[4] = {
0,0,1,-1};
int dy[4] = {