669.修剪二叉搜索树
这道题直接递归三部曲能做
明确递归函数功能:定义递归函数功能为修剪,即在某棵树中寻找节点值在区间[low, high]中的子树,并返回该子树的根节点
确定参数和返回值:参数为待被寻找的树、low 和 high
TreeNode* trimBST(TreeNode* root, int low, int high)
确定递归函数终止条件:如果树为空树,那肯定没有节点值在满足区间条件的子树,返回空即可
if (root == nullptr) return nullptr;
确定递归逻辑: 有如下三种情况。推导这三种情况应该如何操作时记得递归函数功能以及二叉搜索树的特性
情况一
如果根节点的值大于 high,则透明部分表示一定会被修剪掉的, 最后的返回的子树一定是在根节点的左子树进行修剪并返回的左子树的子树。在某棵子树修剪的操作能够调用递归函数本身
if (root->val > high) return trimBST(root->left, low, high);
情况二
如果根节点的值小于 low,则透明部分表示一定会被修剪掉的, 最后的返回的子树一定是在根节点的右子树进行修剪并返回的右子树的子树。在某棵子树修剪的操作能够调用递归函数本身
if (root->val < low) return trimBST(root->right, low, high);
情况三
当不满足上两种情况,则说明根节点的值在区间[low, high]之间,此时,根节点不会被修剪掉,但根节点的左子树或者右子树可能会被修剪。 修剪后 ,根节点的左子树为被修剪后的左子树,根节点的右子树为被修剪后的右子树,在子树中进行修剪的操作能够调用递归函数本身。别忘了最后需要返回修剪后的树
整体代码
/**
* 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:
TreeNode* trimBST(TreeNode* root, int low, int high) { // 定义递归函数功能为在某棵树中寻找节点值在[low, high]中的子树并返回
if (root == nullptr) return nullptr; // 终止条件
// 情况一
if (root->val > high) return trimBST(root->left, low, high);
// 情况二
if (root->val < low) return trimBST(root->right, low, high);
// 情况三
root->left = trimBST(root->left, low, high);
root->right= trimBST(root->right, low, high);
return root;
}
};
108.将有序数组转换为二叉搜索树
注意构造的是高度平衡二叉树!
整体思路还是递归三部曲,涉及到选取根节点然后分割数组,递归构造左子树,递归构造右子树的过程,和之前做的题目挺类似
/**
* 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:
TreeNode* sortedArrayToBST(const vector<int>& nums) {
if (nums.size() == 0) return nullptr;
int index = nums.size() / 2;
TreeNode* root = new TreeNode(nums[index]);
root->left = sortedArrayToBST(vector<int>(nums.begin(), nums.begin() + index));
root->right = sortedArrayToBST(vector<int>(nums.begin() + index + 1, nums.end()));
return root;
}
};
明确递归函数作用是基于有序数组构造高度平衡二叉树就行了,高度平衡二叉树的子树也是高度平衡二叉树,故构造树根的左右子树可通过递归函数本身实现。本题注意选取根节点的原则与数组分割的细节,比较简单就不一步一步分析了
538.把二叉搜索树转换为累加树
二叉搜索树按照左中右(左子树、根节点、右子树)遍历,则依次遍历到的节点是单调递增的
若按照右中左遍历,依次遍历到的节点就是单调递减的
本题就是就是按照右中左遍历,遍历过程中不断把前一个节点的值加到本节点上
又是双指针法,需要一个 pre 记录前一个节点的值
class Solution {
private:
int pre = 0; // 记录前一个节点的数值
void traversal(TreeNode* cur) { // 右中左遍历
if (cur == NULL) return;
traversal(cur->right); // 右
cur->val += pre; // 中
pre = cur->val; // 遍历并处理完了cur,需要将cur存进pre作为“前一个节点”
traversal(cur->left); // 左
}
public:
TreeNode* convertBST(TreeNode* root) {
pre = 0;
traversal(root);
return root;
}
};
回顾总结
二叉搜索树章节结束!回顾一下有哪些技法
在Day14中,我们初探二叉树,着重分析了二叉树的深度优先遍历的实现方式,在递归实现中,引入了递归三部曲的思考方式,同时也了解了迭代实现深度优先遍历需要用到栈
在Day15中,了解了利用队列这种结构实现层序遍历,并掌握了层序遍历模板(两层循环分别遍历层、遍历节点),然后,利用递归三部曲的思维模式实现了一些实际问题
在Day16中,巩固了递归三部曲,探究了一下后序遍历的特点
在Day17和Day18中,初步探索了回溯算法,并区别了一下什么时候用回溯的思考逻辑什么时候用递归三部曲的思考逻辑,即回溯是到了某个节点,下一步该怎么办,而递归三部曲由于需要在单层递归逻辑中利用递归函数本身解决子问题,因此递归三部曲思考逻辑是我在这个节点已经能通过递归函数获取到遍历子节点的结果了,我该怎么向上一层返回
Day20中在遍历二叉树过程中使用双指针,并讨论二叉搜索树的相关问题,强化巩固递归三部曲
Day21和Day22,包括今天的,都是继续巩固之前的几种方法,即:递归三部曲、回溯、遍历树中的双指针,只不过需要很巧妙运用罢了
注意,本章介绍回溯中说了:回溯是思考到了一个节点后应该怎么继续走,就像一个人老老实实从树根走到叶子,撞南墙后再反着走去寻找其他叶子(见Day17)。这种直观的、朴素的暴力搜索思想能解决更多问题,我们将在下一部分详细讲解回溯