算法打卡day20|二叉树篇09|Leetcode 669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

news2024/9/21 22:22:02

   算法题

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))

迭代法

因为二叉搜索树的有序性,不需要使用栈模拟递归的过程。

在剪枝的时候,可以分为三步:

  1. 将root移动到[L, R] 范围内,注意是左闭右闭区间
  2. 剪枝左子树
  3. 剪枝右子树
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接近)


以上是个人的思考反思与总结,若只想根据系列题刷,参考卡哥的网址代码随想录算法官网

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1536969.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NVMe开发——NAND Flash的基本原理

1. 存储单元 1.1. 半导体 半导体&#xff08;semiconductor&#xff09;指常温下导电性能介于导体与绝缘体之间的材料。纯净半导体一般是四价原子(如硅或锗)。 1.1.1. P型半导体 P型半导体是通过将一个纯净的半导体材料&#xff08;如硅或锗&#xff09;中掺杂少量的三价杂…

软件管理rpm与yum

源代码包下载 Compare, Download & Develop Open Source & Business Software - SourceForgehttps://sourceforge.net/ rpm包下载 Welcome to the RPM repository on fr2.rpmfind.nethttp://rpmfind.net/linux/RPM/ 软件包管理 1.rpm包管理: 1)查询: 安装…

Linux网络协议栈从应用层到内核层②

文章目录 1、bind 源码剖析2、listen 源码剖析3、accept 源码剖析4、connect 源码剖析客户端调用connect成功&#xff0c;但三次握手并未完成&#xff0c;进程是如何阻塞自己客户端在connect时&#xff0c;如何选择源端口客户发送syn封包以及重传服务端收到syn封包&#xff0c;…

python通过tcp协议发送二进制数据

写c程序时经常会有发送私有化协议的过程&#xff0c;比如头结构数据包&#xff0c;数据包往往是一个结构体&#xff0c;有时为了方便调试会用python写一些测试程序。 发送的包的结构图示例如下&#xff1a; 接收包的结构图如下&#xff1a; 当然接收的RespBody会很多&#xf…

【Flask开发实战】防火墙配置文件解析(三)之python加工处理

一、前言 上一篇文章中&#xff0c;介绍了通过shell脚本读取配置文件获取到IP地址组、服务端口组、规则清单这三个模块类别基础数据。基础数据中还需要进一步进行展开处理&#xff0c;生成三类扩展表。如IP地址组中&#xff0c;同一个地址组下存在多个IP地址&#xff0c;每组I…

sy4文件、目录操作命令-补充find

补充下find的命令实例把&#xff0c;我搜了下发现这篇文章的笔记符合课程的实例&#xff1a; 参考< How to Find a File in Linux | Find Command - GeeksforGeeks> 这里做了实验&#xff0c;给大家参考&#xff1a; Linux, renowned for its robust command-line int…

【GIT】最好用的git可视化教程网站推荐

最好用可视化学习git 网站:https://learngitbranching.js.org/?demo&localezh_CN 玩遍所有关卡&#xff0c;花半天时间便能掌握git &#x1f603; 本地仓库 基础命令介绍 git commit 提交 git branch <分支名> 创建分支 git checkout <分支名> 切换分支 git…

定制红酒:品质与口感,双重保障

在葡萄酒的世界里&#xff0c;云仓酒庄的洒派定制红酒以其卓着的品质和迷人的口感&#xff0c;成为了无数品鉴者的心头好。这款红酒&#xff0c;不仅是对品质的追求&#xff0c;更是对生活的热爱和品味的体现。 云仓酒庄深知品质是红酒的灵魂&#xff0c;因此对洒派定制红酒的品…

栈和队列的学习

存储方式分两类&#xff1a;顺序存储和链式存储 栈&#xff1a;只允许从一端进行数据插入和删除的线性表&#xff1a;先进后出 FILO 队列&#xff1a;只允许从一端进行数据插入&#xff0c;另一端进行数据删除的线性表&#xff1a;先进先出 FIFO 栈 创建空栈&#xff0c;创建…

wmv转换成mp4能无损吗?这样设置~

WMV和MP4是两种不同的视频格式&#xff0c;它们使用不同的编解码算法和容器格式。在将WMV转换为MP4时&#xff0c;通常会发生一定程度的重新编码&#xff0c;因此不能完全保证无损转换。无损转换意味着输出的MP4文件与输入的WMV文件在视听上没有任何质量损失&#xff0c;这在实…

基于springboot的反诈宣传平台

技术&#xff1a;springbootmysqlvue 一、系统背景 反欺诈平台可以对公交信息进行集中管理&#xff0c;可以真正避免传统管理的缺陷。反欺诈平台是一款运用软件开发技术设计实现的应用系统&#xff0c;在信息处理上可以达到快速的目的&#xff0c;不管是针对数据添加&#xff…

spring cloud项目微服务间互相调用使用自定义标注进行鉴权方案

来吧&#xff0c;贴代码。 一、背景 我们有一个项目使用了spring cloud&#xff0c;有的微服务需要调用别的微服务&#xff0c;但这些调用没有鉴权&#xff1b;当初项目时间非常紧&#xff0c;同时这部分微服务有的对外也没有鉴权&#xff0c;在代码中设置了无须鉴权&#xf…

真机笔记(2)项目分析

目录 1. 项目&#xff1a; 2. 网络工程师工作流程 3. 实验 设备命名 登录密码 使用SSH协议 1. 项目&#xff1a; 竞标方&#xff1a;集成商、厂商、代理商、服务商、监理检测公司 在一个网络项目中&#xff0c;不同的角色承担着不同的职责和任务。以下是集成商、厂商、代…

程序人生——Java异常使用建议

目录 引出异常建议110&#xff1a;提倡异常封装&#xff1b;建议111&#xff1a;采用异常链传递异常 建议112&#xff1a;受检异常尽可能转化为非受检异常建议113&#xff1a;不要在finally块中处理返回值 建议114&#xff1a;不要在构造函数中抛异常建议115&#xff1a;使用Th…

VMD + CEEMDAN 二次分解,CNN-Transformer预测模型

往期精彩内容&#xff1a; 时序预测&#xff1a;LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较-CSDN博客 风速预测&#xff08;一&#xff09;数据集介绍和预处理-CSDN博客 风速预测&#xff08;二&#xff09;基于Pytorch的EMD-LSTM模型-CSDN博客 风速预测&#xff…

Data.olllo:一键数据“分组统计”!

引言&#xff1a; 数据统计是数据分析中的重要环节&#xff0c;而如何快速、准确地进行数据分组统计是许多数据工作者关注的焦点。现在&#xff0c;借助Data.olllo的神奇功能&#xff0c;您可以轻松进行一键式的数据分组统计&#xff0c;为您的数据分析提供更强大的支持&…

什么是浏览器指纹识别?指纹浏览器有用吗?

浏览器指纹识别是好是坏&#xff1f;这现在确实是一个有争议的话题。83%的消费者经常或偶尔会根据浏览历史记录看到广告。其实这就是利用了浏览器指纹技术。 如果您想了解浏览器指纹识别是什么&#xff0c;那就看下去&#xff01; 一、什么是浏览器指纹识别 浏览器指纹是指无…

Quartz完全开发手册(一篇学会Quartz所有知识点)

目录 一、Quartz概念 1.1、Quartz介绍 1.2、使用场景 1.3、特点 二、Quartz运行环境 三、Quartz设计模式 四、Quartz学习的核心概念 4.1、任务Job 4.2、触发器Trigger 4.3、调度器Scheduler 五、Quartz的体系结构与工作流程 5.1、体系结构 5.2、工作流程 六、Quar…

【Mock|JS】Mock的get传参+获取参数信息

mockjs的get传参 前端请求 const { data } await axios("/video/childcomments", {params: {sort: 1,start: 2,count: 5,childCount: 6,commenIndex: 0,},});后端获取参数 使用正则匹配url /*** # 根据url获取query参数* param {Url} urlStr get请求获取参数 eg:…

鸿蒙Harmony应用开发—ArkTS-全局UI方法(时间滑动选择器弹窗)

以24小时的时间区间创建时间滑动选择器&#xff0c;展示在弹窗上。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 本模块功能依赖UI的执行上下文&#xff0c;不可在UI上下文不明确的地方使用&…