算法题
Leetcode 669. 修剪二叉搜索树
题目链接:669. 修剪二叉搜索树
大佬视频讲解:修剪二叉搜索树视频讲解
个人思路
把这道题想复杂了,还想着如何去重构树
解法
递归法
依旧递归三步走
1.确定递归函数的参数以及返回值
这题递归需要返回值,因为是要遍历整棵树,做修改更方便,可以通过递归函数的返回值来移除节点。
2.确定终止条件
修剪的操作并不是在终止条件上进行的,所以就是遇到空节点返回就可以了。
3.确定单层递归的逻辑
如果root(当前节点)的元素小于low的数值,那么应该递归右子树,并返回右子树符合条件的头结点。
如果root(当前节点)的元素大于high的,那么应该递归左子树,并返回左子树符合条件的头结点。
如图:修剪区间[1,3] 将,0小于low,将0的右子树,返回给3节点的左子树。
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.left=trimBST(root.left,low,high);
root.right=trimBST(root.right,low,high);
return root;
}
}
时间复杂度:O(n);(遍历整棵树)
空间复杂度:O(n);(在最坏的情况下,当树是线性链表时(高度接近n),空间复杂度接近O(n))
迭代法
因为二叉搜索树的有序性,不需要使用栈模拟递归的过程。
在剪枝的时候,可以分为三步:
- 将root移动到[L, R] 范围内,注意是左闭右闭区间
- 剪枝左子树
- 剪枝右子树
class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
if(root == null)
return null;
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 = curr.left;
}
curr = root;//回溯
//剪枝右子树
while(curr != null){
while(curr.right != null && curr.right.val > high){
curr.right = curr.right.left;
}
curr = curr.right;
}
return root;
}
}
时间复杂度:O(n);(遍历整棵树)
空间复杂度:O(n);(最坏的情况下,算法的递归调用栈可能达到树的高度)
Leetcode 108.将有序数组转换为二叉搜索树
题目链接:108.将有序数组转换为二叉搜索树
大佬视频讲解:将有序数组转换为二叉搜索树视频讲解
个人思路
根据前面的题,构成二叉搜索树本质就是寻找分割点,分割点作为当前节点,然后递归左区间和右区间,分割点就是数组中间位置的节点。
解法
递归法
首先先定义区间为左闭右闭区间
递归三步走:
1.确定递归函数返回值及其参数
删除二叉树节点,增加二叉树节点,都是用递归函数的返回值来完成,这样是比较方便的。
构造二叉树,依然用递归函数的返回值来构造中节点的左右孩子。
参数的话,首先是传入数组,然后就是左下标left和右下标right,在构造二叉树的时候尽量不要重新定义左右区间数组,而是用下标来操作原数组。
2.确定递归终止条件
这里定义的是左闭右闭的区间,所以当区间 left > right的时候,就是空节点了。
3.确定单层递归的逻辑
首先取数组中间元素的位置,不难写出
int mid = (left + right) / 2;
,这么写其实有一个问题,就是数值越界,例如left和right都是最大int,这么操作就越界了,所以可以这么写:int mid = left + ((right - left) >> 1);
取了中间位置,就开始以中间位置的元素构造节点
接着划分区间,root的左孩子接住下一层左区间的构造节点,右孩子接住下一层右区间构造的节点。
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
TreeNode root = traversal(nums, 0, nums.length - 1);
return root;
}
// 左闭右闭区间[left, right]
private TreeNode traversal(int[] nums, int left, int right) {
if (left > right) return null;
//取中间值;如果数组长度为偶数,中间位置有两个元素,取靠左边的
int mid = left + ((right - left) >> 1);//不会越界的写法
TreeNode root = new TreeNode(nums[mid]);
root.left = traversal(nums, left, mid - 1);
root.right = traversal(nums, mid + 1, right);
return root;
}
}
时间复杂度:O(n);(最差遍历一遍树)
空间复杂度:O(n);(递归树的高度h)
迭代法
通过三个队列来模拟,一个队列放遍历的节点,一个队列放左区间下标,一个队列放右区间下标。
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
if (nums.length == 0) return null;
//根节点初始化
TreeNode root = new TreeNode(-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) >> 1);
// 将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;
}
}
时间复杂度:O(n);(遍历整棵树)
空间复杂度:O(n);(最坏的情况下,队列中可能需要存储整个输入数组的大小数量的节点)
Leetcode 538.把二叉搜索树转换为累加树
题目链接:538.把二叉搜索树转换为累加树
大佬视频讲解:把二叉搜索树转换为累加树视频讲解
个人思路
从题目所给例子树中,可以看出累加的顺序是右中左,所以反中序遍历这个二叉树,然后顺序累加就可以了。
解法
递归法
需要一个pre指针记录当前遍历节点cur的前一个节点,这样才方便做累加。
递归三步走:
1.递归函数参数以及返回值
不需要递归函数的返回值做什么操作,要遍历整棵树。
同时需要定义一个全局变量pre,用来保存cur节点的前一个节点的数值,定义为int型就可以了。
2.确定终止条件
遇空就终止。
3.确定单层递归的逻辑
注意要右中左来遍历二叉树, 中节点的处理逻辑就是让cur的数值加上前一个节点的数值。
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);
}
}
时间复杂度:O(n);(遍历二叉树)
空间复杂度:O(n);(递归树的高度,如果是一个高度不平衡的树(例如链状结构)高度与n接近)
迭代法
中序模板题,用栈模仿递归,遍历二叉树
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;
}
}
时间复杂度:O(n);(遍历二叉树)
空间复杂度:O(n);(递归树的高度,如果是一个高度不平衡的树(例如链状结构)高度与n接近)
以上是个人的思考反思与总结,若只想根据系列题刷,参考卡哥的网址代码随想录算法官网