算法学习笔记(六):二叉树一创建、插入、删除、BFS

一.最近公共祖先

1.二叉搜索树的最近公共祖先

        给定一个二叉搜索树,找到该树种两个指定节点的最近公共祖先

        公共祖先定义:对于有根树 T 的两个节点p、q,最近公共祖先表示为一个节点x,满足x

        是p、q的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)

class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {/**首先定义了二叉搜索树,那么二叉搜索树具有左节点<root<右节点而自己也可以是自己的祖先,所以思路:1.首先判断p、q和根节点比较大小2.如果p、q都大于root,那么只能去右子树去找3.如果p、q都小于root,那么去左子树找4.如果一大一小,那么当前root节点肯定就是最近公共祖先直接返回root即可*///本题不需要判空,因为题目要求p、q一定在树中//而且只有节点在左\右子树中才会去递归,不在就直接返回root了int val = root.val;if (val > p.val && val > q.val) {//在左子树中return lowestCommonAncestor(root.left, p, q);}if (val <p.val && val < q.val) {//在右子树中return lowestCommonAncestor(root.right, p, q);}//一左一右return root;}
}

2.二叉树的最近公共祖先

        给定一个二叉树,找到该树中两个指定节点的最近公共祖先

   

class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {/**该题目明确p、q一定会在树中,那么就有几种情况1.p、q分别在root的左右子树中,就是当前节点的左右子树中各有其中一个那么这个情况下,当前节点就是最近公共祖先2.p、q都在同一侧子树中,要么都在左子树中,要么都在右子树中这个情况下,只要先找到p或者q,那么递归结束,因为p或者q一定在后面,无序递归了,直接返回本身即可   因为自身也可以是自己的祖先*/if (root == null || root == p || root == q) return root;//先找左子树,其实顺序无所谓TreeNode left = lowestCommonAncestor(root.left, p, q);//找右子树TreeNode right = lowestCommonAncestor(root.right, p, q);//如果左边没找到,那么肯定在另一边,直接返回另一边的结果即可if (left == null) return right;//如果右边没找到 那么肯定在另一边if (right == null) return left;//最后在两边找到了 那么当前root就是return root;}
}

    二.二叉搜索树

1.验证二叉搜索树

        给你一个二叉树的根节点 root,判断是否是一个有效的二叉搜索树

        二叉搜索树定义:

        - 节点的左子树只包含小于当前节点的数

        - 节点的右子树只包含大于当前节点的数

        - 所有左子树和右子树自身必须也是二叉搜索树

class Solution {public boolean isValidBST(TreeNode root) {//其实就是递归二叉树 然后判断左是否<root<右即可//要用long,否则有可能节点值正好是int的最大最小值return dfs(root, Long.MIN_VALUE, Long.MAX_VALUE);}private boolean dfs(TreeNode root, long left, long right) {if (root == null) return true;long val = root.val;return left < val && val < right && dfs(root.left, left, val)&& dfs(root.right, val, right);}
}

三.创建二叉树

1.将有序数组转换成二叉搜索树

        给你一个整数数组 nums,其中元素以及按照 升序 排序,请你将其转换为一棵

        平衡 二叉搜索树。

        平衡:是指所有节点的左右子树深度相差不超过 1 

        

class Solution {public TreeNode sortedArrayToBST(int[] nums) {        //这个好办,因为数组以及排好序了//首先搜索树是左<root<右 而平衡是所有节点深度差不超过1//所以一层一层构建过去就行,最多相差1//我们将数组从中间节点一分为2//前半段构建左子树 后半段构建右子树 然后递归构建return dfs(nums, 0, nums.length);}public TreeNode dfs(int[] nums, int left, int right) {if (left == right) return null;//找到中间节点 无符号右移1位 就相当于除以2int mid = (left + right) >>> 1;return new TreeNode(nums[mid], dfs(nums, left, mid), dfs(nums, mid + 1, right));}
}

2.最大二叉树

        给定一个不重复的整数数组 nums,最大二叉树 可以用下面的算法从 nums递归构建

        - 创建一个根节点,其值为 nums 中的最大值

        - 递归的在最大值 左边 的子数组前缀上构建左子树

        - 递归的在最大值右边的子数组后缀上构建右子树

        返回 nums 构建后的 最大二叉树

class Solution {public TreeNode constructMaximumBinaryTree(int[] nums) {/**和排好序的数组构建一样 只不过这次是未排序的数组以及构建一个普通的二叉树 只是要求root需要为最大值那么每次就以最大值为root 找出最大值的index即可*/return dfs(nums, 0, nums.length);}private TreeNode dfs(int[] nums, int left, int right) {if (left == right) return null;//找出最大值int max = left;for (int i = left; i < right; i++) {if (nums[i] > nums[max]) max = i;}//以max为root分成左右构建左右子树return new TreeNode(nums[max], dfs(nums, left, max), dfs(nums, max + 1, right));}
}

3.从前序与中序遍历序列构建二叉树

        给定两个整数数组 preorder 和 inorder,其中 preorder 是二叉树的前序遍历,inorder 是同一

        棵树的中序遍历,请够造二叉树并返回其根节点。

class Solution {public TreeNode buildTree(int[] preorder, int[] inorder) {/**前序+中序可以确定一个二叉树 前序的第一个元素肯定就是根节点然后从preorder第一个元素获取在inorder中的index中序LNR可知,在第一个元素位置左边的肯定是左子树 在右边的肯定是右子树然后就可以确定了树的根节点以及左右子树的范围然后拿到inorder的左子树范围去pre中确定范围先确定左子树在前序遍历中的范围,然后这个范围内的第一个元素就是左子树的跟节点同理右子树在前序遍历中的范围 第一个元素就是右子树的根节点然后递归构建就是了*///首先用哈希表存储中序数组元素的index 前提是数组内元素都是无重复的Map<Integer, Integer> map = new HashMap<>();for (int i = 0; i < inorder.length; i++) {map.put(inorder[i], i);}//递归构建 根节点以及左右子树的两边边界indexreturn dfs(preorder, 0, preorder.length, inorder, 0, inorder.length, map);}public TreeNode dfs(int[] preorder, int preL, int preR, int[] inorder, int inL, int inR, Map<Integer, Integer> map) {if (preL == preR) return null;//前序遍历中的第一个元素肯定是root节点//那么就拿前序遍历中的第一个元素去中序中找位置//找到之后,该位置之前都是左子树,之后都是右子树int leftSize = map.get(preorder[preL]) - inL;//pre+1 因为已经用掉了第一个元素用来构建root 从pre+1开始递归后面的TreeNode left = dfs(preorder, preL + 1, preL + 1 + leftSize, inorder, inL, inL + leftSize, map);//inL + 1的概念是 算右子树范围肯定是左子树范围+根节点本身就是右子树的left起点TreeNode right = dfs(preorder, preL + 1 + leftSize, preR, inorder, inL + 1 + leftSize, inR, map);return new TreeNode(preorder[preL], left, right);}
}

4.从中序与后续遍历序列够造二叉树

        给定两个整数数组 inorder 和 postorder,其中 inorder 是中序遍历,postorder是后续遍历

        请你够造并返回二叉树

class Solution {public TreeNode buildTree(int[] inorder, int[] postorder) {//中序+后续确定一棵二叉树//postorder的最后一个元素是根节点//然后拿这个元素去中序里面确定根节点的位置//然后中序根节点之前是左子树 之后是右子树//然后就是一个和原问题一样的子问题,递归构建//然后其他逻辑跟前中序是一个逻辑 因为在inorder里面左子树的范围是一样的Map<Integer, Integer> map = new HashMap<>();for (int i = 0; i < inorder.length; i++) {map.put(inorder[i], i);}return dfs(postorder, 0, postorder.length, inorder, 0, inorder.length, map);}public TreeNode dfs(int[] postorder, int postL, int postR, int[] inorder, int inL, int inR, Map<Integer, Integer> map) {if (postL == postR) return null;int leftSize = map.get(postorder[postR - 1]) - inL;TreeNode left = dfs(postorder, postL, postL + leftSize, inorder, inL, inL + leftSize, map);TreeNode right = dfs(postorder, postL + leftSize, postR - 1, inorder, inL + 1 + leftSize, inR, map);return new TreeNode(postorder[postR - 1], left, right);}
}

四.插入/删除节点

1.二叉搜索树中的插入操作

        给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value,将值插入二叉搜索树,返回

        插入后二叉搜索树的根节点,输入数据保证,新值和原始二叉搜索树中任意节点值不同

        注意:可能存在多种有效的插入方式,返回任意有效结果即可

class Solution {public TreeNode insertIntoBST(TreeNode root, int val) {/**首先是二叉搜索树 那么满足 L < N < R所以判断插入值和当前root的大小关系大于当前值 插入到右子树中去一直遍历,小于当前值 插入到左子树中去           */if (root == null) return new TreeNode(val);dfs(root, val);//循环解法// TreeNode cur = root;// while (cur != null) {//     if (cur.val < val) {//         //右//         if (cur.right == null) {//             cur.right = new TreeNode(val);//             break;//         }//         cur = cur.right;//     } else {//         //左//         if (cur.left == null) {//             cur.left = new TreeNode(val);//             break;//         }//         cur = cur.left;//     }// }       return root;}private void dfs(TreeNode root, int val) {if (root == null) return;//判断是要插入到左子树还是右子树if (root.val > val) {//左if (root.left == null) {root.left = new TreeNode(val);return;}dfs(root.left, val);} else {//右if (root.right == null) {root.right = new TreeNode(val);return;}dfs(root.right, val);}}
}

五.BFS

1.   二叉树的层序遍历

        给你二叉树的根节点 root,返回其节点值的 层序遍历

        层序遍历就是逐层地从左到右访问所有节点

        

class Solution {public List<List<Integer>> levelOrder(TreeNode root) {/**层序遍历 就是把同一深度的所有节点从左到右顺序输出核心是先进先出 用队列, 然后用当前队列的长度控制遍历队列的次数这样从队列拿到的数据就是当前深度的所有节点首先将root根节点放到队列中去,然后遍历,外部循环判断队列不为空就继续遍历然后内部进行内循环,循环长度就是当前队列的size 然后循环内部进行判断,有子节点就放到队列中去,内循环结束,就表示这一层节点遍历完了,就将数据放到list中去比如:假设当前二叉树是个满二叉树,深度是3,也就是说每个节点都会有左右子节点第一次循环是循环一次,然后此时,队列循环了一次拿走了根节点,然后放入了根节点的2个子节点队列size是2,然后继续循环,循环次数是2,然后每个节点又有2个子节点,此时循环2次结束之后队列size是4,然后循环了2次就将上一层级的节点都拿走了,依次类推,下一次循环4次,结束时队列中就有8个节点因为深度是3 这一层级的节点都没有子节点,循环8次之后队列中就没数据了就退出外循环了这种算法就是广度优先*/List<List<Integer>> result = new ArrayList<>();if (root == null) return result; LinkedList<TreeNode> queue = new LinkedList<>();//根节点入队queue.add(root);//遍历队列while (!queue.isEmpty()) {List<Integer> list = new ArrayList<>();//记录当前queue size 用作内循环int size = queue.size();while (size-- > 0) {//出先进的节点TreeNode node = queue.poll();//随后将出的节点的左右孩子节点放入queuelist.add(node.val);if (node.left != null) queue.add(node.left);if (node.right != null) queue.add(node.right);}result.add(list);}return result;}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp实现开发遇到过的问题(持续更新中....)

1. 在ios模拟器上会出现底部留白的情况 解决方案&#xff1a; 在manifest.json文件&#xff0c;找到开源码视图配置&#xff0c;添加如下&#xff1a; "app-plus" : {"safearea":{"bottom":{"offset" : "none" // 底部安…

Electron开发构建工具electron-vite(alex8088)添加VueDevTools(VitePlugin)

零、介绍 本文章的electron-vite指的是这个项目&#x1f449;electron-vite仓库&#xff0c;electron-vite网站 本文章的VueDevTools指的是VueDevTools的Vite插件版&#x1f449;https://devtools.vuejs.org/guide/vite-plugin 一、有一个用electron-vite创建的项目 略 二、…

机器学习基础05_随机森林线性回归

一、随机森林 机器学习中有一种大类叫集成学习&#xff08;Ensemble Learning&#xff09;&#xff0c;集成学习的基本思想就是将多个分类器组合&#xff0c;从而实现一个预测效果更好的集成分类器。集成算法大致可以分为&#xff1a;Bagging&#xff0c;Boosting 和 Stacking…

Linux驱动开发(9):pinctrl子系统和gpio子系统--led实验

在前面章节&#xff0c;我们有过使用寄存器去编写字符设备的经历了。这种直接在驱动代码中&#xff0c; 通过寄存器映射来对外设进行使用的编程方式&#xff0c;从驱动开发者的角度可以说是灾难。 因为每当芯片的寄存器发生了改动&#xff0c;那么底层的驱动几乎得重写。 那么…

23种设计模式速记法

前言 在软件开发的过程中&#xff0c;设计模式作为解决常见问题的通用模板&#xff0c;一直是开发者的重要工具。尤其是在面临复杂系统架构和需求变化时&#xff0c;设计模式不仅能够提升代码的可复用性和扩展性&#xff0c;还能大大提高团队之间的协作效率。然而&#xff0c;…

IntelliJ+SpringBoot项目实战(十二)--设计项目多模块依赖关系和跨模块调用服务和接口

在非微服务的项目中&#xff0c;一个应用里有多个子系统&#xff0c;例如在一个电商系中&#xff0c;有系统管理子系统、内容管理子系统和电商管理子系统&#xff0c;我们想实现这样的效果&#xff1a; &#xff08;1&#xff09;只需要启动一个SpringBoot应用&#xff0c;不需…

MACOS开发、使用常见问题汇总

MACOS常见问题 本文记录使用macos遇到的常见问题&#xff0c;后面会持续更新&#xff0c;觉得有用的可以收藏一下。 打不开xxx.app&#xff0c;因为它来自身份不明的开发者解决方法(开启任何来源) 打开终端&#xff08;Terminal&#xff09;程序 拷贝sudo spctl --master-di…

【实用数据】上市公司数字化转型双重差分准自然实验数据(2007-2022年)

测算方式&#xff1a; 参考《管理评论》丁相安&#xff08;2024&#xff09;老师研究的做法&#xff0c;企业分批逐步推动自身数字化转型是一个很好的准自然实验&#xff0c;这符合双重差分法的使用情境。 因此&#xff0c;本文使用多时点双重差分模型&#xff08;&#xff24…

PostgreSQL常用字符串函数与示例说明

文章目录 coalesce字符串位置(position strpos)字符串长度与大小写转换去掉空格(trim ltrim rtrim)字符串连接(concat)字符串替换简单替换(replace)替换指定位置长度(overlay)正则替换(regexp_replace) 字符串匹配字符串拆分split_part(拆分数组取指定位置的值)string_to_array…

一次需升级系统的wxpython安装(macOS M1)

WARNING: The scripts libdoc, rebot and robot are installed in /Users/用户名/Library/Python/3.8/bin which is not on PATH. 背景&#xff1a;想在macos安装Robot Framework &#xff0c;显示pip3不是最新&#xff0c;更新pip3后显示不在PATH上 参看博主文章末尾 MAC系统…

细说STM32单片机DMA中断收发RTC实时时间并改善其鲁棒性的另一种方法

目录 一、工程配置 二、软件代码 1、软件代码 2、usart.h 3、usart.c 4、rtc.c 三、运行与调试 1、合规的指令 2、proBuffer[0]不是 ‘#’ 或proBuffer[4]不是 ‘;’ 3、指令长度小于5 4、proBuffer[2]或proBuffer[3]至少一个不是数字 5、; 位于proBuffer…

离散数学---概率, 期望

本文根据 MIT 计算机科学离散数学课程整理&#xff08;Lecture 22 ~ Lecture 24&#xff09;。 1 非负整数期望性质 用 N 表示非负整数集合&#xff0c;R 是 N 上的随机变量&#xff0c;则 R 的期望可以表示成&#xff1a; 证明&#xff1a; 换一个形式&#xff0c;把每一列…

AI一键生成原创花卉印花图案——创新与效率的结合

引言 在时尚界&#xff0c;印花图案一直是设计师们表达创意和个性的重要手段。随着人工智能技术的发展&#xff0c;AI在设计领域的应用越来越广泛&#xff0c;其中就包括了一键生成原创花卉印花图案。本文将探讨AI如何帮助设计师们提高效率&#xff0c;同时保持设计的创新性和…

QT实操中遇到的一些(C++)疑惑点汇总

QT实操中 遇到的一些C疑惑点汇总 1.实例化对象的两种方法及其访问方式 1.1 示例 1.2 总结 2.基类成员的访问 2.1 直接访问继承的基类成员 2.1.1示例代码 2.1.2 输出结果 2.2 使用作用域解析符来显式调用基类成员函数 2.2.1 示例代码 2.2.2 输出结果 2.3 使用 this 指针访问基类…

图形学笔记 - 4. 几何 -网格操作和阴影映射

文章目录 网格操作&#xff1a;几何处理细分Loop细分Catmull-Clark细分&#xff08;一般网格&#xff09;网格简化 阴影 Shadows可视化阴影映射阴影映射阴影贴图的问题 网格操作&#xff1a;几何处理 网格细分网格简化网格正则化 网格细分&#xff08;上采样&#xff09; 网…

OBOO鸥柏车载广告屏:28.6寸液晶一体机的技术革新与应用前景

在数字化迅速发展的今天&#xff0c;OBOO鸥柏推出的28.6寸车载长条形液晶广告屏一体机成为了市场的一大亮点。这款产品不仅具有超窄边框的高亮设计&#xff0c;还利用异形激光切割技术&#xff0c;支持多种形状如圆形、方形及三角形展示&#xff0c;极大地提升了商用和工业屏幕…

Spring Cloud Alibaba、Spring Cloud 与 Spring Boot各版本的对应关系

参考spring-cloud-alibaba github wiki说明&#xff1a;版本说明 下面截取说明&#xff1a; 2022.x 分支 2021.x 分支 2.2.x 分支 组件版本关系

Springboot + vue 健身房管理系统项目部署

1、前言 ​ 许多人在拿到 Spring Boot 项目的源码后&#xff0c;不知道如何运行。我以 Spring Boot Vue 健身房管理系统的部署为例&#xff0c;详细介绍一下部署流程。大多数 Spring Boot 项目都可以通过这种方式部署&#xff0c;希望能帮助到大家。 ​ 2、项目查看 ​ 首…

SOL链上的 Meme 生态发展:从文化到创新的融合#dapp开发#

一、引言 随着区块链技术的不断发展&#xff0c;Meme 文化在去中心化领域逐渐崭露头角。从 Dogecoin 到 Shiba Inu&#xff0c;再到更多细分的 Meme 项目&#xff0c;这类基于网络文化的加密货币因其幽默和社区驱动力吸引了广泛关注。作为近年来备受瞩目的区块链平台之一&…

基于大数据爬虫数据挖掘技术+Python的网络用户购物行为分析与可视化平台(源码+论文+PPT+部署文档教程等)

#1024程序员节&#xff5c;征文# 博主介绍&#xff1a;CSDN毕设辅导第一人、全网粉丝50W,csdn特邀作者、博客专家、腾讯云社区合作讲师、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老…