赞
踩
题目链接: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);(最坏的情况下,算法的递归调用栈可能达到树的高度)
题目链接: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);(最坏的情况下,队列中可能需要存储整个输入数组的大小数量的节点)
题目链接: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接近)
以上是个人的思考反思与总结,若只想根据系列题刷,参考卡哥的网址代码随想录算法官网
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。