Leetcode 669. 修剪二叉搜索树
题目链接:669 修剪二叉搜索树
题干:给你二叉搜索树的根节点
root
,同时给定最小边界low
和最大边界high
。通过修剪二叉搜索树,使得所有节点的值在[low, high]
中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。
思考一:递归法。终止条件:若当前节点为空则返回空。单层递归逻辑:如果当前节点val值大于最大区间值,递归处理其左子树并返回。如果当前节点val值小于最小区间值,递归处理其右子树并返回。如果当前节点val值在区间内,则递归处理左右子树。
代码:
class Solution {
public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (!root) return nullptr;if (root->val > high) //当前节点val值大于区间最大值时递归处理其左子树return trimBST(root->left, low, high);if (root->val < low) //当前节点val值小于区间最小值时递归处理其右子树return trimBST(root->right, low, high);root->left = trimBST(root->left, low ,high);root->right = trimBST(root->right, low ,high);return root;}
};
思考二:迭代法。由于二叉搜索树的节点有序性,遍历过程无需借助栈和队列。先将root移动到区间范围内,再处理其左右子树。
代码:
class Solution {
public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (!root) return nullptr;//处理头节点 让root移动到区间内while (root && (root->val > high || root->val < low)) {if (root->val > high) root = root->left;else root = root->right;}TreeNode* cur = root; //记录根节点//处理左子树while (cur) {while (cur->left && cur->left->val < low)cur->left = cur->left->right;cur = cur->left;}cur = root; //当前节点改为根节点//处理右子树while (cur) {while (cur->right && cur->right->val > high)cur->right = cur->right->left;cur = cur->right;}return root;}
};
Leetcode 108.将有序数组转换为二叉搜索树
题目链接:108 将有序数组转换为二叉搜索树
题干:给你一个整数数组
nums
,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
- 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
思考一:递归法。由于要求高度平衡,则每次递归从数组区间中间位置取值作为节点元素,再以此位置分割数组区间,分别处理左右区间作为当前节点的左右子树。
代码:
class Solution {
public:TreeNode* traversal(vector<int>& nums, int left, int right) {if (left >= right) return nullptr;int middle = (left + right) / 2;TreeNode* root = new TreeNode(nums[middle]);//分割递归处理 左闭右开root->left = traversal(nums, left, middle);root->right = traversal(nums, middle + 1, right);return root;}TreeNode* sortedArrayToBST(vector<int>& nums) {if (nums.size() == 0) return nullptr;return traversal(nums, 0, nums.size());}
};
思考二:迭代法。通过三个队列来模拟递归传值过程,一个队列放遍历的节点,一个队列放左区间下标,一个队列放右区间下标。
代码:
class Solution {
public:TreeNode* sortedArrayToBST(vector<int>& nums) {if (nums.size() == 0) return nullptr;TreeNode* root = new TreeNode(0); // 初始根节点queue<TreeNode*> nodeQue; // 放遍历的节点queue<int> leftQue; // 保存左区间下标queue<int> rightQue; // 保存右区间下标nodeQue.push(root); // 根节点入队列leftQue.push(0); // 0为左区间下标初始位置rightQue.push(nums.size() - 1); // nums.size() - 1为右区间下标初始位置while (!nodeQue.empty()) {TreeNode* curNode = nodeQue.front();nodeQue.pop();int left = leftQue.front(); leftQue.pop();int right = rightQue.front(); rightQue.pop();int mid = left + ((right - left) / 2);curNode->val = nums[mid]; // 将mid对应的元素给中间节点if (left <= mid - 1) { // 处理左区间curNode->left = new TreeNode(0);nodeQue.push(curNode->left);leftQue.push(left);rightQue.push(mid - 1);}if (right >= mid + 1) { // 处理右区间curNode->right = new TreeNode(0);nodeQue.push(curNode->right);leftQue.push(mid + 1);rightQue.push(right);}}return root;}
};
Leetcode 538.把二叉搜索树转换为累加树
题目链接:538 把二叉搜索树转换为累加树
题干:给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点
node
的新值等于原树中大于或等于node.val
的值之和。提醒一下,二叉搜索树满足下列约束条件:
- 节点的左子树仅包含键 小于 节点键的节点。
- 节点的右子树仅包含键 大于 节点键的节点。
- 左右子树也必须是二叉搜索树。
思考一:递归法。由于二叉搜索树的节点有序性,任意节点的右子树键值均大于节点键值。故采取右中左的顺序遍历二叉树,遍历过程顺便记录前一个节点键值。
代码:
class Solution {
public:int pre = 0; //记录前一个结点键值TreeNode* convertBST(TreeNode* root) {if (!root) return nullptr;root->right = convertBST(root->right); //右root->val += pre; //中pre = root->val; //更新键值root->left = convertBST(root->left); //左return root;}
};
思考二:迭代法。修改中序遍历的顺序迭代法的节点遍历顺序为右中左,同时修改中节点处理逻辑为修改键值和更新记录(记录前一个结点键值)。
中序遍历迭代法写法:第十四天| 二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代法
代码:
class Solution {
public:TreeNode* convertBST(TreeNode* root) {stack<TreeNode*> st;TreeNode* cur = root;int pre = 0;while (cur || !st.empty()) {if (cur) {st.push(cur);cur = cur ->right; //右} else {cur = st.top(); st.pop();cur->val += pre; //中pre = cur->val;cur = cur->left; //左}}return root;}
};
二叉树专题总结:
- 熟悉二叉树的前中后序遍历以及层序遍历。
- 递归三部曲的练习:
- 确定递归函数的参数和返回类型
- 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。
- 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。
- 确定终止条件
- 确定单层递归的逻辑
- 考虑每题不同二叉树的各类情况
- 明确是否需要遍历整棵二叉树以及左、中、右节点的遍历顺序
- 确定递归函数的参数和返回类型
- 迭代方式,采用栈和队列来辅助。其中的统一迭代法采用标记法来确定节点是否访问过。
- 构造二叉树要坚持区间不变量原则。注意类似用数组构造二叉树的题目,每次分隔尽量不要定义新的数组,而是通过下标索引直接在原数组上操作,这样可以节约时间和空间上的开销。
- 二叉树题目总结:
-
涉及到二叉树的构造,无论普通二叉树还是二叉搜索树一定前序,都是先构造中节点。
-
求普通二叉树的属性,一般是后序,一般要通过递归函数的返回值做计算。
-
求二叉搜索树的属性,一定是中序,利用好节点的有序性。
-