广搜和深搜的题目
问题一:
题目描述
在二叉树中,根节点位于深度 0 处,每个深度为 k 的节点的子节点位于深度 k+1 处。
如果二叉树的两个节点深度相同,但 父节点不同 ,则它们是一对堂兄弟节点。
我们给出了具有唯一值的二叉树的根节点 root ,以及树中两个不同节点的值 x 和 y 。
只有与值 x 和 y 对应的节点是堂兄弟节点时,才返回 true 。否则,返回 false。
示例 1:

输入:root = [1,2,3,4], x = 4, y = 3
输出:false
示例 2:

输入:root = [1,2,3,null,4,null,5], x = 5, y = 4
输出:true
示例 3:

输入:root = [1,2,3,null,4], x = 2, y = 3
输出:false
提示:
二叉树的节点数介于 2 到 100 之间。
每个节点的值都是唯一的、范围为 1 到 100 的整数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/cousins-in-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解析:
用深搜的思路
-
深搜就是前序遍历这棵状态树,每往下一层那么深度就加一,如果在某一层找到了目标值,那么就返回这层的父节点和深度,所以这颗状态树的状态就是(父节点和深度)。
-
如果存在两个状态相同的结点,那么这两个结点就是堂兄弟结点。
代码演示
class Solution {
public:
struct data{
data(TreeNode* n,TreeNode* fa,int dep):father(fa),node(node),depth(dep){}
int depth;
TreeNode* father;
TreeNode* node;
};
int dfs(TreeNode* root,int x,TreeNode*& father){
if(!root)return -1;
if(root->val == x)return 0;
int l;
father = root;
if(root->left)l = dfs(root->left,x,father);
if(l != -1)return l+1;
father = root;
if(root->right)l = dfs(root->right,x,father);
if(l != -1)return l+1;
return -1;
}
bool isCousins(TreeNode* root, int x, int y) {
int d1 = -1;
int d2 = -1;
TreeNode* left_fa = nullptr;
TreeNode* right_fa = nullptr;
/*
分别寻找两个目标值所对应的状态
*/
d1 = dfs(root,x,right_fa);
d2 = dfs(root,y,left_fa);
return d1 == d2 && left_fa != right_fa;
}
};
提交结果:

用广搜的思路
- 如果要用广搜,那么就要用一个队列存储状态树的每一层的状态点。
- 状态点所存储的状态就是每一个点所对应的父节点和所在的深度。
代码演示
class Solution {
public:
//设置状态
struct data{
data(TreeNode* node,TreeNode* fa,int dep):node(node),father(fa),depth(dep){}
int depth;
TreeNode* node;
TreeNode* father;
};
//设置一个广搜队列作为全局变量
queue<data> q;
bool bfs(TreeNode* root,int x,int y){
int d1 = -1;
int d2 = -1;
TreeNode* left_fa = NULL;
TreeNode* right_fa = NULL;
while(!q.empty()){
data cur = q.front();
if(cur.node->val == x)d1 = cur.depth,left_fa = cur.father;
if(cur.node->val == y)d2= cur.depth,right_fa = cur.father;
if(cur.node->left)q.push(data(cur.node->left,cur.node,cur.depth+1));
if(cur.node->right)q.push(data(cur.node->right,cur.node,cur.depth+1));
q.pop();
}
return d1 == d2 && left_fa != right_fa;
}
bool isCousins(TreeNode* root, int x, int y) {
q.push(data(root,nullptr,0));
return bfs(root,x,y);
}
};

比较发现对于树的查找遍历操作两种方式的时间都是一样的,这是因为两种搜索的本质分别是层序遍历和后序遍历,所以所用的时间复杂度一样,从而消耗的时间也是一样的。
问题2:
给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。
两个相邻元素间的距离为 1 。
示例 1:

输入:mat = [[0,0,0],[0,1,0],[0,0,0]]
输出:[[0,0,0],[0,1,0],[0,0,0]]
示例 2:

输入:mat = [[0,0,0],[0,1,0],[1,1,1]]
输出:[[0,0,0],[0,1,0],[1,2,1]]
提示:
m == mat.length
n == mat[i].length
1 <= m, n <= 104
1 <= m * n <= 104
mat[i][j] is either 0 or 1.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/01-matrix
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路:
- 这就是对图的遍历,所以可以用深搜或者广搜解决。我们现在先用广搜解决
- 题目说的是每一个不为零的点到零点的最近距离,因为不为零的点有很多,所以正难则反,我们可以换一个思路:每一个零点向外扩展到每个不是0的点的距离是多少?
- 设置状态点,每个点的坐标(x,y),同时还有每个点的深度(k),这个点的深度是上一个状态点的深度+1.
- 装入初始化状态:初始状态就是每一个0点。所以刚开始可以在初始队列中装入每一个0状态点。
- 处理各种状态:
- 如果该状态点的下一个状态的坐标值已经超过边界,那么就直接跳过。
- 如果该状态点的下一个状态点时0状态点,那么也是直接跳过,因为我是计算该点到非零点之间的距离。
- 如果这个点已经遍历过,那么就是直接跳过。
- 在自己建立的地图中记录下遍历到这一位置的深度(k)
- 将所遍历到的下一个状态点装入队列。
代码演示:
class Solution {
public:
//设置化状态点
struct data{
data(int x = 0,int y = 0,int k = 0):x(x),y(y),k(k){}
int k;
int x;
int y;
} ;
void init_queue(queue<data>&q,int row,int col,vector<vector<int>>mat,vector<vector<int>>& vis){
for(int i = 0;i<row;i++){
vis.push_back(vector<int>());
for(int j = 0;j<col;j++){
vis[i].push_back(-1);
if(mat[i][j])continue;
vis[i][j] = 0;
q.push(data(i,j,0));
}
}
return;
}
vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
queue<data>q;
int row = mat.size();
int col = mat[0].size();
vector<vector<int>>vis;
init_queue(q,row,col,mat,vis);
//方向数组
int dir[4][2] = {0,1,0,-1,1,0,-1,0};
while(!q.empty()){
data cur = q.front();
int x , y;
for(int i = 0;i < 4; i++){
x = cur.x + dir[i][0];
y = cur.y + dir[i][1];
if(x < 0 || x >= row)continue;
if(y < 0 || y >= col)continue;
//如果不等于-1就说明这个状态点已经被遍历过了,所以就直接跳过。
if(vis[x][y] != -1)continue;
//此时记录这个位置的深度就是上一个点的深度+1
vis[x][y] = cur.k + 1;
q.push(data(x,y,cur.k+1));
}
//弹出用完了的这个状态点
q.pop();
}
return vis;
}
};
输出结果:

题目描述:
给你一个 n x n 的二进制矩阵 grid 中,返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径,返回 -1 。
二进制矩阵中的 畅通路径 是一条从 左上角 单元格(即,(0, 0))到 右下角 单元格(即,(n - 1, n - 1))的路径,该路径同时满足下述要求:
路径途经的所有单元格都的值都是 0 。
路径中所有相邻的单元格应当在 8 个方向之一 上连通(即,相邻两单元之间彼此不同且共享一条边或者一个角)。
畅通路径的长度 是该路径途经的单元格总数。
示例 1:

输入:grid = [[0,1],[1,0]]
输出:2
示例 2:

输入:grid = [[0,0,0],[1,1,0],[1,1,0]]
输出:4
示例 3:
输入:grid = [[1,0,0],[1,1,0],[1,1,0]]
输出:-1
提示:
n == grid.length
n == grid[i].length
1 <= n <= 100
grid[i][j] 为 0 或 1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-path-in-binary-matrix
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路描述:
- 设置状态点:每一个点的位置还有遍历到这个点时的深度。
- 提取状态点
- 处理状态点:
- 如果超过了边界就跳过
- 如果已经走过了就跳过
- 如果已经到达终点就返回终点状态点的深度
- 保存下一个状态点。
代码演示:
class Solution {
public:
struct data{
data(int x = 0,int y = 0,int k = 0):x(x),y(y),k(k){}
int x;
int y;
int k;
};
int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
//因为可以向斜线方向移动,所以有8个方向
int dir[8][2] = {0,1,0,-1,1,0,-1,0,1,1,-1,-1,1,-1,-1,1};
if(grid[0][0] == 1)return -1;
vector<vector<int>>vis;
//记录走过的每一个路径,如果走过就记为1,没走过就记为-1
for(int i = 0;i<grid.size();i++){
vis.push_back(vector<int>());
for(int j = 0;j<grid[0].size();j++){
vis[i].push_back(-1);
}
}
queue<data>q;
//计入初始状态
q.push(data(0,0,1));
while(!q.empty()){
data cur = q.front();
for(int i = 0;i<8;i++){
int x = cur.x+dir[i][0];
int y = cur.y+dir[i][1];
//如果当前状态就是终点状态,就直接返回该点的深度
if(cur.x == grid.size() - 1 && cur.y == grid[0].size() - 1){
return cur.k;
}
if(x < 0 || x >= grid.size())continue;
if(y < 0 || y >= grid[0].size())continue;
if(grid[x][y] == 1)continue;
if(vis[x][y] != -1)continue;
vis[x][y] = 1;
q.push(data(x,y,cur.k+1));
}
q.pop();
}
return -1;
}
};
输出结果:

题目描述:转盘锁
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 。每个拨轮可以自由旋转:例如把 '9' 变为 '0','0' 变为 '9' 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 '0000' ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。
示例 1:
输入:deadends = ["0201","0101","0102","1212","2002"], target = "0202"
输出:6
解释:
可能的移动序列为 "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202"。
注意 "0000" -> "0001" -> "0002" -> "0102" -> "0202" 这样的序列是不能解锁的,
因为当拨动到 "0102" 时这个锁就会被锁定。
示例 2:
输入: deadends = ["8888"], target = "0009"
输出:1
解释:
把最后一位反向旋转一次即可 "0000" -> "0009"。
示例 3:
输入: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888"
输出:-1
解释:
无法旋转到目标数字且不被锁定。
示例 4:
输入: deadends = ["0000"], target = "8888"
输出:-1
提示:
1 <= deadends.length <= 500
deadends[i].length == 4
target.length == 4
target 不在 deadends 之中
target 和 deadends[i] 仅由若干位数字组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/open-the-lock
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
- 首先设置状态点,因为他要寻找到目标字符串输出寻找到这个状态点时走过的最短距离,所以状态点就要保存该点的字符串和走到这个点时的路程。
- 首先存入状态点,就是“0000”字符串的状态。
- 读取状态点
- 扩展状态点,四个位置,每次扩展一位,那一位的数字要么加一,要么减一。如果扩展出来的那个点已经遍历过或者是个禁止点,就直接跳过。
- 存入新的状态点
代码演示
class Solution {
public:
//扩展状态,某一位上的数字加一
string get_S(string str,int i,int k){
if(str[i] + k > '9'){
str[i] = '0';
return str;
}
else if(str[i] + k < '0'){
str[i] = '9';
return str;
}
str[i]+=k;
return str;
}
//这只状态点,保存当前字符串和走到这个字符串的路程
struct data{
data(string s,int h):str(s),h(h){}
int h;
string str;
};
int openLock(vector<string>& deadends, string target) {
unordered_set<string> s;
//先将所有禁止的字符串点存入set,等下一旦发现存在就直接跳过。
for(auto sh:deadends){
s.insert(sh);
}
if(s.find("0000") != s.end()){
return -1;
}
queue<data>q;
q.push(data("0000",0));
while(!q.empty()){
data cur = q.front();
string temp;
int flag;
for(int i = 0;i<4;i++){
for(int j = 0;j<2;j++){
if(j)flag = 1;
else flag = -1;
//扩展状态
if(cur.str == target)return cur.h;
temp = get_S(cur.str, i, flag);
if(s.find(temp) != s.end())continue;
s.insert(temp);
q.push(data(temp,cur.h + 1));
}
}
q.pop();
}
return -1;
}
};
结果:

题目描述:机器人的运动范围
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
示例 1:
输入:m = 2, n = 3, k = 1
输出:3
示例 2:
输入:m = 3, n = 1, k = 0
输出:1
提示:
1 <= n,m <= 100
0 <= k <= 20
通过次数186,230提交次数353,234
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路说明:
- 目标状态点是各个位上的数的和值等于给定的范围。所以各个状态点要保存每个状态点的对应下标
- 存入状态到状态队列中。
- 提取状态并且扩展状态,其中如果这个点走过了或者这个点已经超过了边界,也是直接跳过。
- 存入扩展后的新的状态点
代码演示
class Solution {
public:
struct data{
data(int x,int y):x(x),y(y){}
int x,y;
};
int get_int(int x,int y){
int sum = 0;
while(x){
sum+=x%10;
x/=10;
}
while(y){
sum+=y%10;
y/=10;
}
return sum;
}
int dir[4][2] = {0,1,0,-1,1,0,-1,0};
int movingCount(int m, int n, int k) {
queue<data>q;
vector<vector<int>> vis;
for(int i = 0;i<m;i++){
vis.push_back(vector<int>());
for(int j = 0;j<n;j++){
vis[i].push_back(-1);
}
}
int sum = 1;
q.push(data(0,0));
vis[0][0] = 0;
while(!q.empty()){
data cur = q.front();
int x,y;
for(int i = 0;i<4;i++){
x = cur.x+dir[i][0];
y = cur.y+dir[i][1];
if(x < 0 || x >= m)continue;
if(y < 0 || y >= n)continue;
if(vis[x][y] != -1)continue;
if(get_int(x,y) > k)continue;
vis[x][y] = 0;
cout<<get_int(x,y)<<endl;
sum++;
q.push(data(x,y));
}
q.pop();
}
return sum;
}
};
输出结果:

题目描述:被环绕的区域
给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。
示例 1:

输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
示例 2:
输入:board = [["X"]]
输出:[["X"]]
提示:
m == board.length
n == board[i].length
1 <= m, n <= 200
board[i][j] 为 'X' 或 'O'
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/surrounded-regions
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码演示
class Solution {
public:
struct data{
data(int x,int y):x(x),y(y){}
int x,y;
};
void solve(vector<vector<char>>& board) {
int col,row;
row = board.size();
int dir[4][2] = {0,1,0,-1,1,0,-1,0};
col = board[0].size();
queue<data>q;
for(int i = 0;i<row;i++){
if(board[i][col-1] == 'O'){
q.push(data(i,col-1));
board[i][col - 1] = 'o';
}
if(board[i][0] == 'O'){
q.push(data(i,0));
board[i][0] = 'o';
}
}
for(int i = 0;i<col;i++){
if(board[0][i] == 'O'){
q.push(data(0,i));
board[0][i] = 'o';
}
if(board[row - 1][i] == 'O'){
q.push(data(row - 1,i));
board[row - 1][i] = 'o';
}
}
while(!q.empty()){
data cur = q.front();
int x,y;
for(int i = 0;i<4;i++){
x = cur.x + dir[i][0];
y = cur.y + dir[i][1];
if(x < 0 || x >= row)continue;
if(y < 0 || y >= col)continue;
if(board[x][y] == 'X')continue;
if(board[x][y] == 'o')continue;
board[x][y] = 'o';
q.push(data(x,y));
}
q.pop();
}
for(int i = 0;i<row;i++){
for(int j = 0;j<col;j++){
if(board[i][j] == 'o')board[i][j] = 'O';
else board[i][j] = 'X';
}
}
return ;
}
};
实验结果:


浙公网安备 33010602011771号