目录
本节的三道题都蛮好的,不是很难,但是自己也没法完全写出来,需要再看再尝试!
以后做题先试递归法
669. 修剪二叉搜索树
669递归法代码写出来好简洁,自己思考的时候很混乱,多看一下!
[1, 3]区间在二叉搜索树的中可不是单纯的节点3和左孩子节点0就决定的,还要考虑节点0的右子树。
递归法(需掌握)
- 确定单层递归的逻辑
如果root(当前节点)的元素小于low的数值,那么应该递归右子树(因为右子树中存在不符合要求的节点),并返回右子树符合条件的头结点。
如果root(当前节点)的元素大于high的,那么应该递归左子树,并返回左子树符合条件的头结点。
然后将下一层处理完左子树的结果赋给root->left,处理完右子树的结果赋给root->right。
最后返回root节点。
class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
// 递归法
if (root == null) return null;
if (root.val < low) {
return trimBST(root.right, low, high);
}
if (root.val > high) {
return trimBST(root.left, low, high);
}
// root在[low,high]范围内
root.left = trimBST(root.left, low, high);
root.right = trimBST(root.right, low, high);
return root;
}
}
迭代法
因为二叉搜索树的有序性,不需要使用栈模拟递归的过程。
在剪枝的时候,可以分为三步:
- 将root移动到[L, R] 范围内,注意是左闭右闭区间
- 剪枝左子树
- 剪枝右子树
逐步修剪不符合条件的节点,确保每个节点都在指定范围内。
class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
// 迭代法
if (root == null) return null;
// 处理头结点,让root移动到[L, R] 范围内,注意是左闭右闭
while (root != null && (root.val < low || root.val > high)) {
if (root.val < low)
root = root.right;
else
root = root.left;
}
TreeNode curr = root;
while (curr != null) { // 处理左子树
while (curr.left != null && curr.left.val < low) {
curr.left = curr.left.right; // 检查 curr.left 是否存在且值小于 low,如果是,就将 curr.left 更新为其右子树
}
curr = curr.left;
}
//go back to root
curr = root;
while (curr != null) { // 处理右子树
while (curr.right != null && curr.right.val > high) {
curr.right = curr.right.left;
}
curr = curr.right;
}
return root;
}
}
108. 将有序数组转换为二叉搜索树
本题注意是 平衡 二叉搜索树
一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
本题就是寻找分割点,分割点作为当前节点,然后递归左区间和右区间。分割点就是数组中间位置的节点。
递归法(简单)
递归三部曲:
- 确定递归函数返回值及其参数
(在构造二叉树的时候尽量不要重新定义左右区间数组,而是用下标来操作原数组。)
// 左闭右闭区间[left, right]
TreeNode* traversal(vector<int>& nums, int left, int right)
- 确定递归终止条件
这里定义的是左闭右闭的区间,所以当区间 left > right的时候,就是空节点了。
if (left > right) return nullptr;
- 确定单层递归的逻辑
首先取数组中间元素的位置,不难写出int mid = (left + right) / 2;
,这么写其实有一个问题,就是数值越界,例如left和right都是最大int,这么操作就越界了,在二分法中尤其需要注意!
所以可以这么写:int mid = left + ((right - left) / 2);
但本题leetcode的测试数据并不会越界,所以怎么写都可以。但需要有这个意识!
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
// 递归法: 左闭右闭 [left,right]
TreeNode root = traversal(nums, 0, nums.length - 1);
return root;
}
private TreeNode traversal(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;
}
}
迭代法
没搞懂这道题的迭代法的逻辑。以及最后返回root等等。可能受限于现在的JAVA基础没学完。
看递归法吧,好理解。后续再来理解一下迭代法。
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
// 迭代法: 左闭右闭 [left,right]
if (nums.length == 0) return null;
//根节点初始化
TreeNode root = new TreeNode(-1); // 值初始化为 -1(实际上这个值在后面会被替换)。这是为了方便后续操作。
Queue<TreeNode> nodeQueue = new LinkedList<>(); //存储待处理的树节点。
Queue<Integer> leftQueue = new LinkedList<>(); //存储对应节点的左右区间的下标。
Queue<Integer> rightQueue = new LinkedList<>();
// 根节点入队列
nodeQueue.offer(root);
// 0为左区间下标初始位置
leftQueue.offer(0);
// nums.size() - 1为右区间下标初始位置
rightQueue.offer(nums.length - 1);
while (!nodeQueue.isEmpty()) {
// 取出当前节点及其左右区间下标。
TreeNode currNode = nodeQueue.poll();
int left = leftQueue.poll();
int right = rightQueue.poll();
int mid = left + (right - left) / 2;
// 将mid对应的元素给中间节点
currNode.val = nums[mid];
// 处理左区间
if (left <= mid - 1) {
currNode.left = new TreeNode(-1);
nodeQueue.offer(currNode.left);
leftQueue.offer(left);
rightQueue.offer(mid - 1);
}
// 处理右区间
if (right >= mid + 1) {
currNode.right = new TreeNode(-1);
nodeQueue.offer(currNode.right);
leftQueue.offer(mid + 1);
rightQueue.offer(right);
}
}
return root;
}
}
538. 把二叉搜索树转换为累加树(需再看!掌握,简单)
这道题说实话我自己都没看懂题目是啥意思。看了题解后才搞懂。
一棵树,换一个角度来看,这就是一个有序数组[2, 5, 13],求从后到前的累加数组,也就是[20, 18, 13]。
思路:从树中可以看出累加的顺序是右中左,所以我们需要反中序遍历这个二叉树,然后顺序累加就可以了。
递归法(需掌握)
双指针操作:pre,cur。
需要一个pre指针记录当前遍历节点cur的前一个节点,这样才方便做累加。
注意要右中左来遍历二叉树, 中节点的处理逻辑就是让cur的数值加上前一个节点的数值。
用sum一样的。
class Solution {
int sum;
public TreeNode convertBST(TreeNode root) {
// 递归法
sum = 0;
convertBST1(root);
return root;
}
// 按右中左顺序遍历,累加即可
public void convertBST1(TreeNode root) {
if (root == null) return;
convertBST1(root.right); // 右
sum += root.val; // 中
root.val = sum;
convertBST1(root.left); // 左
}
}
迭代法
迭代法其实就是中序模板题了
class Solution {
public TreeNode convertBST(TreeNode root) {
// 迭代法
int pre = 0;
Stack<TreeNode> stack = new Stack<>();
if (root == null) return null;
stack.add(root);
while (!stack.isEmpty()) {
TreeNode curr = stack.peek();
//curr != null的状况,只负责存node到stack中
if (curr != null) {
stack.pop();
if (curr.left != null) //左
stack.add(curr.left);
stack.add(curr); //中
stack.add(null);
if (curr.right != null) //右
stack.add(curr.right);
} else {
//curr == null的状况,只负责做单层逻辑
stack.pop();
TreeNode temp = stack.pop();
temp.val += pre;
pre = temp.val;
}
}
return root;
}
}
二叉树总结
二叉树总结篇看这里:二叉树总结
-
涉及到二叉树的构造,无论普通二叉树还是二叉搜索树一定前序,都是先构造中节点。
-
求普通二叉树的属性,一般是后序,一般要通过递归函数的返回值做计算。
-
求二叉搜索树的属性,一定是中序了,要不白瞎了有序性了。
后续复盘有空再来做这几道。
二叉树终于结束了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
第二十一天的总算是结束了,直冲Day22!