提示:努力生活,开心、快乐的一天
文章目录
108. 将有序数组转换为二叉搜索树
💡解题思路
- 取数组中间节点作为分割点,然后递归左区间和右区间
- 递归三部曲:
- 确定递归函数返回值及其参数:传入数组,然后就是左下标left和右下标right,定义的是左闭右闭区间,在不断分割的过程中,也会坚持左闭右闭的区间,这又涉及到我们讲过的循环不变量
- 确定递归终止条件:区间是左闭右闭,所以当区间 left > right的时候,就是空节点了
- 确定单层递归的逻辑:首先取数组中间元素的位置,注意数值越界问题,划分区间,root的左孩子接住下一层左区间的构造节点,右孩子接住下一层右区间构造的节点,最后返回root节点
- 迭代法:通过三个队列来模拟,一个队列放遍历的节点,一个队列放左区间下标,一个队列放右区间下标。
🤔遇到的问题
- 注意分割的区间,是左闭右闭
- 迭代法,while(nodeQue)和while (nodeQue.length)的区分
💻代码实现
递归法
var sortedArrayToBST = function(nums) {
const buildTree = (arr, left, right) => {
if (left > right) return null
let mid = Math.floor(left + (right - left) / 2)
//取数组中间位置的值为树的根节点
let root = new TreeNode(arr[mid])
//root的左孩子接住下一层左区间的构造节点
root.left = buildTree(arr, left, mid - 1)
//右孩子接住下一层右区间构造的节点
root.right = buildTree(arr, mid + 1, right)
return root
}
//左闭右闭
return buildTree(nums,0,nums.length-1)
};
迭代法
var sortedArrayToBST = function (nums) {
if (nums.length === 0) return null
let root = new TreeNode(0)//初始根节点
let nodeQue = [root] //放遍历的节点,并初始化
let leftQue = [0] //放左区间的下标,初始化
let rightQue = [nums.length - 1] // 放右区间的下标
while (nodeQue.length) {
let curNode = nodeQue.pop()
let left = leftQue.pop()
let right = rightQue.pop()
let mid = Math.floor(left + (right - left) / 2)
//将下标为mid的元素给中间节点
curNode.val = nums[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. 把二叉搜索树转换为累加树
💡解题思路
- 从树中可以看出累加的顺序是右中左,所以我们需要反中序遍历这个二叉树,然后顺序累加就可以了,需要一个pre指针记录当前遍历节点cur的前一个节点
- 递归遍历三部曲:
- 递归函数参数以及返回值:因为要遍历整棵树,不需要递归函数的返回值做什么操作
- 确定终止条件:遇空就终止
- 确定单层递归的逻辑:右中左来遍历二叉树, 中节点的处理逻辑就是让cur的数值加上前一个节点的数值
- 迭代法:也是需要一个pre指针记录当前遍历节点cur的前一个节点,同时需要一个栈来存放节点
🤔遇到的问题
- pre的节点需要定义在递归外面
- 迭代法需要一直找到最右的节点
💻代码实现
递归法
var convertBST = function (root) {
// 记录前一个节点的数值
let pre = 0
const addNode = (node) => {
if (node) {
//右
addNode(node.right)
//中
node.val += pre
pre = node.val
//左
addNode(node.left)
}
}
addNode(root)
return root
};
迭代法
var convertBST = function (root) {
let pre = 0;
let cur = root;
let stack = [];//存放遍历的节点
while (cur !== null || stack.length !== 0) {
//找到最大的节点
while (cur !== null) {
stack.push(cur);
//右
cur = cur.right;
}
//中
cur = stack.pop();
cur.val += pre;
pre = cur.val;
//左
cur = cur.left;
}
return root;
};
🎯题目总结
累加树,想清楚思路后,比其他二叉树的题回简单很多,终点需要注意:反中序遍历
669. 修剪二叉搜索树
💡解题思路
- 特别注意:如下图**[1, 3]区间在二叉搜索树的中可不是单纯的节点3和左孩子节点0就决定的,还要考虑节点0的右子树**,图中我们发现节点0并不符合区间要求,那么将节点0的右孩子 节点2 直接赋给 节点3的左孩子就可以了(就是把节点0从二叉树中移除)
- 递归三部曲:
- 确定递归函数的参数以及返回值:参数:root,low,high;需要返回值:返回的是删除某节点后需要传给上一层的值,比如,叶子节点被删除,返回给上一层null,中间节点被删除,返回的是该节点的孩子节点给上一层
- 确定终止条件:修剪的操作并不是在终止条件上进行的,所以就是遇到空节点返回就可以了
- 确定单层递归的逻辑:
1、如果root(当前节点)的元素小于low的数值,那么应该递归右子树,并返回右子树符合条件的头结点;如上图中,相当于把节点0的右孩子(节点2)返回给上一层
2、如果root(当前节点)的元素大于high的,那么应该递归左子树,并返回左子树符合条件的头结点。
3、将下一层处理完左子树的结果赋给root->left,处理完右子树的结果赋给root->right,如上图,相当于用节点3的左孩子 把下一层返回的 节点0的右孩子(节点2) 接住
🤔遇到的问题
- 逻辑有点绕,需要捋一下
💻代码实现
递归法
var trimBST = function(root, low, high) {
if (root === null) return null
//root(当前节点)的元素小于low的数值,那么应该递归右子树,并返回右子树符合条件的头结点。
if (root.val < low) {
return trimBST(root.right,low,high)// 寻找符合区间[low, high]的节点
}
//root(当前节点)的元素大于high的,那么应该递归左子树,并返回左子树符合条件的头结点。
if (root.val > high) {
return trimBST(root.left,low,high)// 寻找符合区间[low, high]的节点
}
root.left = trimBST(root.left, low, high)// root->left接入符合条件的左孩子
root.right = trimBST(root.right, low, high)// root->right接入符合条件的右孩子
return root
};
🎯题目总结
思路很难想到,看完视频和文章以后次啊能自己想明白,但细想一下有点绕,但不难
🎈今日心得
二叉树最后一天了,感觉该复习一下啦,这个周末的休息日,就好好复习一下二叉树吧