> 作者:დ旧言~
> 座右铭:松树千年终是朽,槿花一日自为荣。> 目标:了解什么是深搜,并且掌握深搜算法。
> 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安!
> 专栏选自:递归、搜索与回溯算法_დ旧言~的博客-CSDN博客
> 望小伙伴们点赞👍收藏✨加关注哟💕💕
一、算法讲解
在解决⼀个规模为 n 的问题时,如果满足以下条件,我们可以使用递归来解决:
- 问题可以被划分为规模更小的子问题,并且这些子问题具有与原问题相同的解决方法。
- 当我们知道规模更小的子问题(规模为 n - 1)的解时,我们可以直接计算出规模为 n 的问题
- 的解。
- 存在⼀种简单情况,或者说当问题的规模足够小时,我们可以直接求解问题。
⼀般的递归求解过程如下:
- 验证是否满足简单情况。
- 假设较小规模的问题已经解决,解决当前问题。
上述步骤可以通过数学归纳法来证明。
二、算法习题
2.1 第一题
题目描述:
题目解析:
后序遍历按照左⼦树、根节点、右⼦树的顺序遍历⼆叉树的所有节点,通常⽤于⼆叉搜索树相关题
⽬。
算法思路:
如果⼀棵树是⼆叉搜索树,那么它的中序遍历的结果⼀定是⼀个严格递增的序列。因此,我们可以初始化⼀个⽆穷⼩的全区变量,⽤来记录中序遍历过程中的前驱结点。那么就可以在中序遍历的过程中,先判断是否和前驱结点构成递增序列,然后修改前驱结点为当前结点,传⼊下⼀层的递归中。
算法流程:
1. 初始化⼀个全局的变量 prev,用来记录中序遍历过程中的前驱结点的 val。
2. 中序遍历的递归函数中:
a. 设置递归出⼝:root == nullptr 的时候,返回 true;
b. 先递归判断左⼦树是否是⼆叉搜索树,⽤ retleft 标记;
c. 然后判断当前结点是否满⾜⼆叉搜索树的性质,⽤ retcur 标记:
- 如果当前结点的 val ⼤于 prev,说明满⾜条件,retcur 改为 true;
- 如果当前结点的 val ⼩于等于 prev,说明不满⾜条件,retcur 改为 false;
d. 最后递归判断右⼦树是否是⼆叉搜索树,⽤ retright 标记;
3. 只有当 retleft、 retcur 和 retright 都是 true 的时候,才返回 true。
代码呈现:
class Solution {
long prev = LONG_MIN;
public:
bool isValidBST(TreeNode* root)
{
if (root == nullptr)
return true;
bool left = isValidBST(root->left);
// 剪枝
if (left == false)
return false;
bool cur = false;
if (root->val > prev)
cur = true;
// 剪枝
if (cur == false)
return false;
prev = root->val;
bool right = isValidBST(root->right);
return left && right && cur;
}
};
2.2 第二题
题目描述:
算法思路:
我们可以根据中序遍历的过程,只需扫描前 k 个结点即可,因此,我们可以创建⼀个全局的计数器 count,将其初始化为 k,每遍历⼀个节点就将 count--。直到某次递归的时候,count 的值等于 1,说明此时的结点就是我们要找的结果。
算法流程:
定义⼀个全局的变量 count,在主函数中初始化为 k 的值(不⽤全局也可以,当成参数传⼊递归过
程中)
递归函数的设计:
int dfs(TreeNode* root):返回值为第 k 个结点。
递归函数流程(中序遍历):
1. 递归出⼝:空节点直接返回 -1,说明没有找到。
2. 去左⼦树上查找结果,记为 retleft:
- 如果 retleft == -1,说明没找到,继续执⾏下⾯逻辑。
- 如果 retleft != -1,说明找到了,直接返回结果,⽆需执⾏下⾯代码(剪枝)。
3. 如果左⼦树没找到,判断当前结点是否符合:
- 如果符合,直接返回结果
4. 如果当前结点不符合,去右⼦树上寻找结果。
代码呈现:
class Solution {
int count;
int ret;
public:
int kthSmallest(TreeNode* root, int k)
{
count = k;
dfs(root);
return ret;
}
void dfs(TreeNode* root)
{
if (root == nullptr || count == 0)
return;
dfs(root->left);
count--;
if (count == 0)
ret = root->val;
dfs(root->right);
}
};
2.3 第三题
题目描述:
算法思路:使⽤深度优先遍历(DFS)求解。
路径以字符串形式存储,从根节点开始遍历,每次遍历时将当前节点的值加⼊到路径中,如果该节点为叶⼦节点,将路径存储到结果中。否则,将 "->" 加⼊到路径中并递归遍历该节点的左右子树。
定义⼀个结果数组,进⾏递归。递归具体实现⽅法如下:
- 如果当前节点不为空,就将当前节点的值加⼊路径 path 中,否则直接返回。
- 判断当前节点是否为叶⼦节点,如果是,则将当前路径加⼊到所有路径的存储数组 paths 中。
- 否则,将当前节点值加上 "->" 作为路径的分隔符,继续递归遍历当前节点的左右⼦节点。
- 返回结果数组。
特别地,我们可以只使⽤⼀个字符串存储每个状态的字符串,在递归回溯的过程中,需要将路径中
的当前节点移除,以回到上⼀个节点。
具体实现⽅法如下:
1. 定义⼀个结果数组和⼀个路径数组。
2. 从根节点开始递归,递归函数的参数为当前节点、结果数组和路径数组。
- 如果当前节点为空,返回。
- 将当前节点的值加⼊到路径数组中。
- 如果当前节点为叶⼦节点,将路径数组中的所有元素拼接成字符串,并将该字符串存储到结果
- 数组中。
- 递归遍历当前节点的左⼦树。
- 递归遍历当前节点的右⼦树。
- 回溯,将路径数组中的最后⼀个元素移除,以返回到上⼀个节点。
3. 返回结果数组。
代码呈现:
class Solution {
public:
vector<string> ret; // 记录结果
vector<string> binaryTreePaths(TreeNode* root)
{
string path;
if (root == nullptr)
return ret;
dfs(root, path);
return ret;
}
void dfs(TreeNode* root, string path)
{
path += to_string(root->val);
if (root->left == nullptr && root->right == nullptr)
{
ret.push_back(path);
return;
}
path += "->";
if (root->left)
dfs(root->left, path);
if (root->right)
dfs(root->right, path);
}
};
三、结束语
今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。