算法导论(递归回溯)——⼆叉树中的深搜
算法思路(129)
前序遍历遵循“根节点、左子树、右子树”的顺序遍历二叉树的所有节点,常用于解决子节点状态依赖于父节点状态的问题。
算法思路:
在前序遍历的过程中,我们可以将信息从节点向左右子树传递,并在回溯时获取左右子树的返回值。递归函数实现了两个主要功能:
- 整合父节点的值与当前节点的信息,计算当前节点的数字,并将结果传递到下一层进行递归。
- 当遇到叶子节点时,不再向下传递信息,而是将整合后的结果向上回溯至根节点。
在递归结束后,根节点的返回值将更新为整棵树的数字和。
算法流程:
递归函数设计:int dfs(TreeNode* root, int num)
- 返回值:当前子树的计算结果(数字和)。
- 参数:
num
在递归过程中向下传递的父节点的数字信息。 - 功能:整合父节点与当前节点的信息,计算当前节点的数字,并向下传递,同时在回溯时返回以当前节点为根的子树的数字和。
递归函数流程:
- 若当前节点为空,表示路径结束,返回0。
- 将父节点传递下来的信息与当前节点的值结合,计算当前节点的数字和
sum
。 - 如果当前节点是叶子节点,则直接返回整合后的结果
sum
。 - 如果当前节点不是叶子节点,将
sum
传递到左右子树,分别计算左右子树的数字和,并将结果相加后返回。
C++:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution
{
public:
int sumNumbers(TreeNode* root)
{
return dfs(root, 0);
}
int dfs(TreeNode* root, int presum)
{
presum = presum * 10 + root->val;
if(root->left == nullptr && root->right == nullptr)
return presum;
int ret = 0;
if(root->left) ret += dfs(root->left, presum);
if(root->right) ret += dfs(root->right, presum);
return ret;
}
};
Java:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution
{
public int sumNumbers(TreeNode root)
{
return dfs(root, 0);
}
public int dfs(TreeNode root, int preSum)
{
preSum = preSum * 10 + root.val;
if(root.left == null && root.right == null)
return preSum;
int ret = 0;
if(root.left != null) ret += dfs(root.left, preSum);
if(root.right != null) ret += dfs(root.right, preSum);
return ret;
}
}
算法思路(98)
如果一棵树是二叉搜索树(BST),那么它的中序遍历结果一定是一个严格递增的序列。因此,我们可以初始化一个全局变量 prev
用以记录中序遍历过程中的前驱节点。通过在中序遍历过程中判断当前节点与前驱节点的值是否构成递增序列,我们可以有效地验证树是否满足二叉搜索树的特性。
算法流程:
-
初始化一个全局变量
prev
,用于记录中序遍历过程中前驱节点的值(初始值可以设为负无穷)。 -
中序遍历的递归函数流程如下:
- 递归出口:当
root == nullptr
时,返回true
,表示空树是二叉搜索树。 - 先递归检查左子树是否满足二叉搜索树的特性,结果存储在
retleft
中。 - 然后检查当前节点是否满足二叉搜索树的性质,结果存储在
retcur
中:- 如果当前节点的值
val
大于prev
,则满足二叉搜索树的条件,将retcur
标记为true
并更新prev
为当前节点的值。 - 如果当前节点的值
val
小于或等于prev
,则不满足条件,将retcur
标记为false
。
- 如果当前节点的值
- 最后递归检查右子树是否满足二叉搜索树的特性,结果存储在
retright
中。
- 递归出口:当
-
只有当
retleft
、retcur
和retright
全部为true
时,才能返回true
,说明当前树满足二叉搜索树的所有条件。
C++:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
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;
}
};
Java:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution
{
long prev = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root)
{
if(root == null) return true;
boolean left = isValidBST(root.left);
// 剪枝
if(left == false) return false;
boolean cur = false;
if(root.val > prev) cur = true;
if(cur == false) return false;
prev = root.val;
boolean right = isValidBST(root.right);
return left && cur && right;
}
}
算法思路(230)
我们可以利用中序遍历的特性,只需扫描前 k
个节点便可找到目标。因此,我们可以创建一个全局计数器 count
,初始化为 k
,在每次遍历节点时将 count
减一。当 count
的值减至 1
时,表明当前节点就是我们要查找的结果。
算法流程:
-
定义一个全局变量
count
,在主函数中初始化为k
(也可以作为参数传入递归)。 -
递归函数设计:
int dfs(TreeNode* root)
- 返回值为第
k
个节点。
- 返回值为第
-
递归函数流程(中序遍历):
- 递归出口:如果当前节点为空,直接返回
-1
,表示未找到。 - 先递归遍历左子树查找结果,结果存储在
retleft
中:- 如果
retleft == -1
,则表示在左子树中未找到,继续执行后续逻辑。 - 如果
retleft != -1
,则表示已找到第k
个节点,直接返回结果,无需继续执行后续代码(进行剪枝)。
- 如果
- 如果左子树未找到,判断当前节点:
- 如果当前节点是我们要找的目标(
count == 1
),则返回当前节点。
- 如果当前节点是我们要找的目标(
- 如果当前节点不符合条件,则递归遍历右子树寻找结果。
- 递归出口:如果当前节点为空,直接返回
C++:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
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);
}
};
Java:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
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 == null || count == 0) return;
dfs(root.left);
count--;
if(count == 0) ret = root.val;
if(count == 0) return;
dfs(root.right);
}
}