669.修剪二叉搜索树
题目
给你二叉搜索树的根节点 root
,同时给定最小边界low
和最大边界 high
。通过修剪二叉搜索树,使得所有节点的值在[low, high]
中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。
所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。
示例 1:
输入:root = [1,0,2], low = 1, high = 2
输出:[1,null,2]
提示:
-
树中节点数在范围
[1, 10(4)]
内 -
0 <= Node.val <= 10(4)
-
树中每个节点的值都是 唯一 的
-
题目数据保证输入是一棵有效的二叉搜索树
-
0 <= low <= high <= 10(4)
思路
在上图中,发现节点 0 并不符合区间要求,那么将节点 0 的右孩子节点 2 直接赋给节点 3 的左孩子就可以(即把节点 0 从二叉树中移除)
递归三部曲:
-
确定递归函数的参数和返回值
- 遍历整棵树做修改,最后返回 TreeNode*
- 传入根节点和 low, high
-
确定终止条件
- 修剪的操作并不是在终止条件上进行的,所以是遇到空节点返回就可以了
-
确定单层递归逻辑
- 如果 root(当前节点)的元素小于 low 的数值,递归右子树,并返回右子树符合条件的头节点
- 如果 root 元素大于 high,递归左子树,并返回左子树符合条件的头节点
- 将下一层处理完左子树的结果赋给 root->left,处理完右子树的结果赋给 root->right,最后返回 root 节点
代码实现
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(root == nullptr) return nullptr;
if(root->val < low){
TreeNode* right = trimBST(root->right, low, high);
return right;
}
if(root->val > high){
TreeNode* left = trimBST(root->left, low, high);
return left;
}
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}
};
精简后:
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if (root == nullptr) return nullptr;
if (root->val < low) return trimBST(root->right, low, high);
if (root->val > high) return trimBST(root->left, low, high);
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}
};
108.将有序数组转换为二叉搜索树
题目
给你一个整数数组 nums
,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
示例 1:
输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:
提示:
-
1 <= nums.length <= 10(4)
-
-10(4) <= nums[i] <= 10(4)
-
nums
按 严格递增 顺序排列
思路
递归三部曲
-
确定递归函数返回值以及参数
- 用递归函数的返回值来构造中节点的左右孩子
- 参数,传入数组的左下标 left 和右下标 right
-
确定递归终止条件
- 区间定义左闭右闭,当区间 left > right,是空节点
-
确定单层递归逻辑
- 取数组中间元素
int mid = left + ((right - left) / 2);// 这么写不会越界
- 以中间位置的元素构造节点
TreeNode* root = new TreeNode(nums[mid]);
,如果是偶数长度的数组,取靠左边的 - 划分区间,root 的左孩子接住下一层左区间的构造节点,右孩子同理
- 最后返回 root
- 取数组中间元素
注意:在调用traversal的时候为什么传入的left和right为什么是0和nums.size() - 1,因为定义的区间为左闭右闭。
代码实现
class Solution {
private:
TreeNode* traversal(vector<int>& nums, int left, int right){
if(left > right) return NULL;
int mid = left + ((right - left) / 2);// 这么写不会越界
TreeNode* root = new TreeNode(nums[mid]);
root->left = traversal(nums, left, mid - 1);
root->right = traversal(nums, mid + 1, right);
return root;
}
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
return traversal(nums, 0, nums.size() -1);
}
};
538.把二叉搜索树转换为累加树
题目
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node
的新值等于原树中大于或等于 node.val
的值之和。
提醒一下,二叉搜索树满足下列约束条件:
-
节点的左子树仅包含键 小于 节点键的节点。
-
节点的右子树仅包含键 大于 节点键的节点。
-
左右子树也必须是二叉搜索树。
示例 1:
输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]
提示:
-
树中的节点数介于
0
和10(4)
( )之间。 -
每个节点的值介于
-10(4)
和10(4)
之间。 -
树中的所有值 互不相同 。
-
给定的树为二叉搜索树。
思路
其实就是从最后一个叶子节点开始,不断往前累加,所以反中序遍历就可以了
递归函数三部曲:
-
递归函数参数以及返回值
- 不需要返回值,要遍历整棵树
- 参数需要定义一个全局变量 pre,用来保存 cur 节点的前一个节点的数值,定义为 int 型
-
确定终止条件
- 遇空就终止
-
确定单层递归逻辑
- 用右中左来遍历二叉树,中节点的处理逻辑就是让 cur 的数值加上前一个节点的数值
代码实现
class Solution {
private:
int pre = 0;
void traversal(TreeNode* cur){
if(cur == NULL) return;
traversal(cur->right);
cur->val += pre;
pre = cur->val;
traversal(cur->left);
}
public:
TreeNode* convertBST(TreeNode* root) {
int pre = 0;
traversal(root);
return root;
}
};
二叉树总结
复习链接:
https://programmercarl.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E6%80%BB%E7%BB%93%E7%AF%87.html#%E9%98%B6%E6%AE%B5%E6%80%BB%E7%BB%93
遍历顺序:
-
涉及到二叉树的构造,如论普通还是二叉搜索树,一定前序,都是先构造中节点
-
求普通二叉树的属性,一般是后序,一般要通过递归函数的返回值做计算
- 如果单纯求深度就用前序
-
求二叉搜索树的属性,一定是中序,合理利用有序性