题目链接: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;
}
};
题目链接:108 将有序数组转换为二叉搜索树
题干:给你一个整数数组
nums
,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
- 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
思考一:递归法。由于要求高度平衡,则每次递归从数组区间中间位置取值作为节点元素,再以此位置分割数组区间,分别处理左右区间作为当前节点的左右子树。
代码:
class Solution {
public:
TreeNode* traversal(vector& 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& nums) {
if (nums.size() == 0) return nullptr;
return traversal(nums, 0, nums.size());
}
};
思考二:迭代法。通过三个队列来模拟递归传值过程,一个队列放遍历的节点,一个队列放左区间下标,一个队列放右区间下标。
代码:
class Solution {
public:
TreeNode* sortedArrayToBST(vector& nums) {
if (nums.size() == 0) return nullptr;
TreeNode* root = new TreeNode(0); // 初始根节点
queue nodeQue; // 放遍历的节点
queue leftQue; // 保存左区间下标
queue 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;
}
};
题目链接: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 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;
}
};
二叉树专题总结:
涉及到二叉树的构造,无论普通二叉树还是二叉搜索树一定前序,都是先构造中节点。
求普通二叉树的属性,一般是后序,一般要通过递归函数的返回值做计算。
求二叉搜索树的属性,一定是中序,利用好节点的有序性。