LeetCode刷题日记之二叉树(六)

news2024/11/25 8:54:48

目录

  • 前言
  • 二叉搜索树中的众数
  • 二叉树的最近公共祖先
  • 二叉搜索树的最近公共祖先
  • 总结


前言

又是学习LeetCode二叉树的新一天,今天还是接着学习一下二叉搜索树的内容,希望博主记录的内容能够对大家有所帮助 ,一起加油吧朋友们!💪💪💪


二叉搜索树中的众数

LeetCode题目链接

给一个含重复值的二叉搜索树的根节点root,返回该树的所有众数(以数组格式)
请添加图片描述

思路梳理:这道题其实也是可以利用二叉搜素数中序遍历节点值递增的规律来写,因为就是可以在递归遍历的时候去更新当前节点频率和最大节点频率来完成这样,当当前节点频率大于等于最大节点频率时就把当前节点的值加入到一个整数列表中🤔🤔🤔

我们接下来梳理递归三要素

  • 确定递归的参数和返回值
// 定义了结果的列表所以无需返回值,然后前序节点值和当前频率、最大频率用变量保存故参数只有数的根节点即可
private void inOrderTraversal(TreeNode root){}
  • 确定递归的出口
//因为要处理每个节点所以会进入空节点
if(root == null) return;
  • 确定递归的单层逻辑
//递归单层处理逻辑
inOrderTraversal(root.left);// 递归遍历左子树

if(root.val == preVal){ // 处理当前节点
    curCount++;
}else{
    curCount = 1;
}

if(curCount > maxCount){  // 判断是否需要更新众数列表
    maxCount = curCount;
    result.clear();
    result.add(root.val);
}
else if(curCount == maxCount){// 如果当前频率等于最大频率也加入到众数列表中
    result.add(root.val);
}
preVal = root.val;// 更新前一个节点的值为当前值


inOrderTraversal(root.right);// 递归遍历右子树

完整代码如下

/**递归法 */
class Solution {
    private int maxCount = 0; //最大频率
    private int curCount = 0;//当前频率
    private int preVal = Integer.MIN_VALUE;//记录前一个节点的值
    private List<Integer> result = new ArrayList<>();//记录众数结果
    public int[] findMode(TreeNode root) {
        inOrderTraversal(root);
        return result.stream().mapToInt(Integer::intValue).toArray();
    }

    private void inOrderTraversal(TreeNode root){ //中序遍历
        //递归出口
        if(root == null) return;

        //递归单层处理逻辑
        inOrderTraversal(root.left);

        if(root.val == preVal){ //更新频率
            curCount++;
        }else{
            curCount = 1;
        }

        if(curCount > maxCount){ //更新众数
            maxCount = curCount;
            result.clear();
            result.add(root.val);
        }
        else if(curCount == maxCount){
            result.add(root.val);
        }
        preVal = root.val;//更新存储的前节点的值


        inOrderTraversal(root.right);
        
    }
}

这里可以提一下的是最后将列表转换成整型数组的一步操作

return result.stream().mapToInt(Integer::intValue).toArray();
  • .stream()是java 8引入的Stream API中的一个方法,用于从集合( 如 ListSetMap 等)或数组生成一个流(Stream)🤔
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()  // 生成流
     .filter(name -> name.startsWith("A"))  // 过滤出以"A"开头的名字
     .forEach(System.out::println);  // 输出满足条件的名字
  • mapToInt() 是 Java Stream API 提供的用于将流中的对象映射为基本类型的方法,类似的方法还有mapToDouble()mapToLong()等。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
int[] result = numbers.stream()
                      .mapToInt(Integer::intValue) // 将 Integer 转换为 int
                      .toArray(); // 转换为 int[]
// result 是 [1, 2, 3, 4]
  • intValue()Integer 类的一个方法,它的作用是将 Integer 对象转换为基本数据类型 int。在 Java 中,包装类(如 IntegerDouble 等)都有类似的转换方法,将包装类的对象转换为对应的基本数据类型。
result.stream().mapToInt(Integer::intValue).toArray();
result.stream().mapToDouble(Double::doubleValue).toArray();
result.stream().mapToLong(Long::longValue).toArray();

我们也来梳理一下迭代法

  • 主要就是中序遍历需要先把root节点压栈,然后把左子树节点递归入栈,然后弹出root节点进行处理,处理完后递归处理右节点,处理的逻辑和递归一样,然后的话就是栈一开始为空,循环条件加上一个当前节点不为空,而当前节点初始化为root🤔🤔🤔
/**迭代法 */
class Solution {
    public int[] findMode(TreeNode root) {
         int maxCount = 0; //最大频率
         int curCount = 0;//当前频率
         int preVal = Integer.MIN_VALUE;//记录前一个节点的值
         List<Integer> result = new ArrayList<>();//记录众数结果
         Stack<TreeNode> stack = new Stack<>();
         TreeNode cur = root;
         while(cur != null || !stack.isEmpty()){
            if(cur != null){ //中序遍历得先把中和左给压栈
                stack.push(cur);
                cur = cur.left;
            }else{
                cur = stack.pop(); //弹出中节点
                if(cur.val == preVal){
                    curCount++;
                }else{
                    curCount = 1;
                }

                if(curCount > maxCount){
                    maxCount = curCount;
                    result.clear();
                    result.add(cur.val);
                }else if(curCount == maxCount){
                    result.add(cur.val);
                }
                preVal = cur.val;
                cur = cur.right;//处理右节点
            }
         }
         return result.stream().mapToInt(Integer::intValue).toArray();
    }

    
}

二叉树的最近公共祖先

LeetCode题目链接

给定一个二叉树,找到该树中两个指定节点的最近公共祖先
请添加图片描述

我们来进行思路梳理🤔🤔🤔

  • 首先的话就是找最近公共祖先我们需要从底向上遍历二叉树,所以只能通过后序遍历(左右中)来实现从底向上遍历🤔🤔🤔
递归函数返回值 递归函数(递归参数给定节点p,递归参数给定节点q,递归参数二叉树根节点root){
	//递归出口
	//处理左子树
	//处理右子树
	//处理中间父结点
}
  • 接着的话最重要的就是怎么判断中间父结点是不是给定节点p和q的最近公共祖先呢🤔🤔🤔,如果左子树与右子树分别找到q/p则当前root就是最近的公共祖先
    请添加图片描述

  • 那有人会说了如果root是q/p呢,这个就属于递归出口的判断逻辑了,首先我们会判断每个节点也必然会进行空节点判断所以递归出口有if(root == null)return null;,如果root为q/p那么也得返回找到的root节点然后呢?🤔🤔🤔接着往其左右子树去找剩下的节点能不能找到

TreeNode 递归函数(递归参数给定节点p,递归参数给定节点q,递归参数二叉树根节点root){
	if(root == q || root == p || root == null) return root;//递归出口
	//处理左子树
	//处理右子树
	//处理中间父结点
}

思路梳理完毕后我们来紧接着梳理递归三要素🤔🤔🤔

  • 确定递归函数的参数和返回值
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {}
  • 确定递归出口
if(root == q || root == p || root == null) return root;
  • 确定递归的单层处理逻辑
/**单层处理逻辑 */
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);//后序遍历
if(left != null && right != null) return root;
if(left == null) return right;
return left;

递归法java代码如下

/**递归法 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == q || root == p || root == null) return root;

        /**单层处理逻辑 */
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);//后序遍历
        if(left != null && right != null) return root;
        if(left == null) return right;
        return left;
    }
}

我们接着来梳理迭代法的思路🤔🤔🤔

  • 我们可以利用栈进行深度优先搜索来遍历二叉树,同时记录每个节点的父结点信息以便在找到p/q节点时能够追溯路径🤔🤔🤔
HashMap<TreeNode,TreeNode> parentMap = new HashMap<>();//哈希表存储每个节点的父节点
  • 找到p和q节点时遍历其中一个节点的路径,返回第一个相交的节点作为最近公共祖先🤔🤔🤔
HashSet<TreeNode> ancestors = new HashSet<>();//准备处理p/q节点的全部父节点路径
while(p != null){ //列出p的所有祖先节点
    ancestors.add(p);
    p = parentMap.get(p);
}

while(!ancestors.contains(q)){//查找p的所有祖先节点中的第一个公共祖先
    q = parentMap.get(q);
}

return q;

完整迭代法代码如下

/**迭代法 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return null;//提前结束
        HashMap<TreeNode,TreeNode> parentMap = new HashMap<>();//哈希表存储每个节点的父节点
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);//准备前序遍历
        parentMap.put(root, null); //根节点没有父结点

        while(!parentMap.containsKey(q) || !parentMap.containsKey(p)){ //直到路径找到p和q
            TreeNode cur = stack.pop();

            if(cur.left != null){//处理左子树
                parentMap.put(cur.left, cur);
                stack.push(cur.left);
            }

            if(cur.right != null){//处理右子树
                parentMap.put(cur.right, cur);
                stack.push(cur.right);
            }
        }

        HashSet<TreeNode> ancestors = new HashSet<>();//准备处理p/q节点的全部父节点路径
        while(p != null){ //列出p的所有祖先节点
            ancestors.add(p);
            p = parentMap.get(p);
        }

        while(!ancestors.contains(q)){//查找p的所有祖先节点中的第一个公共祖先
            q = parentMap.get(q);
        }

        return q;
    }

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

LeetCode题目链接

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先
请添加图片描述

我们来进行思路梳理🤔🤔🤔

  • 首先的话就是找最近公共祖先我们需要从底向上遍历二叉树,所以只能通过后序遍历(左右中)来实现从底向上遍历,这个逻辑不变🤔🤔🤔

  • 接着的话最重要的就是怎么判断中间父结点是不是给定节点p和q的最近公共祖先呢🤔🤔🤔,如果左子树与右子树分别找到q/p则当前root就是最近的公共祖先,对于二叉搜索树的话,根节点的值大于左子树的值小于右子树的值,所以我们可以得到要找的最近公共祖先就是数组在[q.val, p.val]或者[p.val, q.val]之中

ok,我们直接梳理递归三要素

  • 确定递归参数和返回值
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {}
  • 确定递归出口
if(root == null) return root;
  • 确定单层处理逻辑
if(root.val > q.val && root.val > p.val){
	return lowestCommonAncestor(root.left, p, q);//往左找
} else if(root.val < q.val && root.val < p.val){
    return lowestCommonAncestor(root.right, p, q);//往左找
}else{
    return root;//找到了
}

总的递归代码如下

/**递归法 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return root;
        if(root.val > q.val && root.val > p.val){
	        return lowestCommonAncestor(root.left, p, q);//往左找
        } else if(root.val < q.val && root.val < p.val){
            return lowestCommonAncestor(root.right, p, q);//往左找
        }else{
            return root;//找到了
        }
    }
}

我们接着来梳理迭代法的逻辑🤔🤔🤔

  • 由于二叉搜索树的性质,我们递归本质上是去找一个数值介于p与q数值之间的节点,所以我们迭代法无需栈或者队列来存储节点🤔🤔🤔

迭代法代码如下

/**迭代法 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        while(root != null){
            if(root.val > q.val && root.val > p.val) {
                root = root.left;
            }else if(root.val < q.val && root.val < p.val){
                root = root.right;
            }else{
                return root;
            }
        }
        return null;
    }
}

总结

国庆假期结束,开始加油啦✊✊✊

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

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

相关文章

【ubuntu】ubuntu20.04安装conda

1.下载 安装参考&#xff1a;https://blog.csdn.net/weixin_44119391/article/details/128577681 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 2.安装 sudo chmod 777 -R ./Anaconda3-5.3.1-Linux-x86_64.sh ./Anaconda3-5.3.1-Linux-x86_64.sh Enter键确认安装…

图的基本概念 - 离散数学系列(五)

目录 1. 图的定义 节点与边 2. 度与路径 节点的度 路径与圈 3. 图的连通性 连通图与非连通图 强连通与弱连通 连通分量 4. 实际应用场景 1. 社交网络 2. 城市交通系统 3. 网络结构 5. 例题与练习 例题1&#xff1a;节点的度 例题2&#xff1a;判断连通性 练习题…

计算机视觉中的3D变换:让虚拟与现实无缝对接

嘿&#xff0c;小伙伴们&#xff01;今天咱们聊聊计算机视觉中的3D变换&#xff0c;这是连接虚拟世界与现实世界的桥梁&#xff01;无论你是想为游戏开发增添真实感&#xff0c;还是希望在增强现实中实现精准定位&#xff0c;这篇教程都会让你受益匪浅。准备好了吗&#xff1f;…

python数据分析与可视化工具介绍-matplotlib库

众所周知&#xff0c;python的数据分析库主要是numpy&#xff0c;pandas&#xff0c;和matplotlib&#xff0c;而前面两个主要是数据处理工具库&#xff0c;最后一个才是真正的作图展示工具库。本节来学习一下matploatlib工具库的使用。 Matplotlib常用绘图函数 pyplot简介 m…

Kubernetes: kube-proxy 和 CNI 是如何协作的?

在 Kubernetes 中&#xff0c;kube-proxy 和 CNI 插件协同工作&#xff0c;确保集群内 Pod 之间的互联互通。 Kube-proxy & CNI 如上图所示&#xff0c;假设我们有一个类型为 ClusterIP 的 Service&#xff0c;它对应两个位于不同节点的 Pod。 当我们从 Pod A 对该 Servi…

C语言的柔性数组

目录 柔性数组1.柔性数组的特点&#xff1a;2.柔性数组的使用3.柔性数组的优势 柔性数组 也许你从来没有听说过柔性数组&#xff08;flexible array&#xff09;这个概念&#xff0c;但是它确实是存在的。 C99 中&#xff0c;结构体中的最后⼀个元素允许是未知⼤⼩的数组&…

MFC工控项目实例二十三模拟量输入设置界面

承接专栏《MFC工控项目实例二十二主界面计数背景颜色改变》 1、在SenSet.h文件中添加代码 #include "BtnST.h" #include "ShadeButtonST.h"/ // SenSet dialogclass SenSet : public CDialog { // Construction public:SenSet(CWnd* pParent NULL); //…

aws(学习笔记第三课) AWS CloudFormation

aws(学习笔记第三课) 使用AWS CloudFormation 学习内容&#xff1a; AWS CloudFormation的模板解析使用AWS CloudFormation启动ec2 server 1. AWS CloudFormation 的模版解析 CloudFormation模板结构 CloudFormation是AWS的配置管理工具&#xff0c;属于Infrastructure as Co…

黑马javaWeb笔记重点备份2:mybatis基础(注解方式)、数据库连接池概念、lombok使用

以下均来自&#xff1a;【黑马程序员JavaWeb开发教程&#xff0c;实现javaweb企业开发全流程&#xff08;涵盖SpringMyBatisSpringMVCSpringBoot等&#xff09;】 https://www.bilibili.com/video/BV1m84y1w7Tb/?p75&share_sourcecopy_web&vd_source9332b8fc5ea8d349a…

AI 激活新势能,中小企业全媒体营销绽放无限可能

什么是全媒体营销&#xff1a; 全媒体营销是一种利用多种媒介渠道进行品牌、产品或服务推广的营销策略。它结合了传统媒体&#xff08;如电视、广播、报纸、杂志&#xff09;和新媒体&#xff08;如互联网、社交媒体、移动应用等&#xff09;的优势&#xff0c;以实现信息的广…

vivado 使用 UltraFast 设计方法系统级设计流程图

下图展示了 Vivado Design Suite 中包含的各种设计步骤以及特性。您可以通过赛灵思 Documentation Navigator“Design Hub View” 访问该图的互动版&#xff0c;单击每个步骤将链接至相关资源。 理解 UltraFast 设计方法概念 在设计开始初期就采取正确方法非常重要&#xf…

数据结构与算法笔记:概念与leetcode练习题

1、数组Array 时间复杂度 数组访问&#xff1a;O(1) 数组搜索&#xff1a;O(N) 数组插入&#xff1a;O(N) 数组删除&#xff1a;O(N) 特点 适合读&#xff0c;不适合写 数组常用操作 # 1、创建数组 a [] # 2、尾部添加元素 a.append(1) a.append(2) a.append(3) # 3、…

《Linux从小白到高手》理论篇:Linux的系统环境管理

List item 值此国庆佳节&#xff0c;深宅家中&#xff0c;闲来无事&#xff0c;就多写几篇博文。本篇详细深入介绍Linux的系统环境管理。 环境变量 linux系统下&#xff0c;如果你下载并安装了应用程序&#xff0c;很有可能在键入它的名称时出现“command not found”的提示…

大语言模型(LLM)综述

大语言模型&#xff08;LLM&#xff09;综述 正如缩放定律&#xff08;Scaling Laws &#xff09;所预测的那样, LLM 的通用语言理解和生成能力是通过在大量文本数据上训练数十亿个模型参数获得的。基于 Transformer 的大型语言模型 (LLM) 的最新进展&#xff08;在 Web 规模的…

系统架构设计师⑧:软件工程-软件开发方法与模型

系统架构设计师⑧&#xff1a;软件工程-软件开发方法与模型 软件开发方法 常用的软件开发方法主要分为3类&#xff1a; 结构化法&#xff08;比如C语言开发-面向过程&#xff09;&#xff0c; 面向对象法&#xff08;比如C或者JAVA开发-面向对象&#xff09;&#xff0c; 面向…

永洪科技第八届全国用户大会,释放数据价值!

永洪科技&#xff0c;作为“致力于打造全球领先的数据技术厂商”&#xff0c;将于【2024年11月1日】&#xff0c;在【北京东方君悦大酒店】盛大召开“第八届永洪科技全国用户大会”。旨在通过AIBI的深入融合&#xff0c;更加智能且精准的展现及预测未来的数据走向&#xff0c;展…

10.7学习

1.安全认证 ●Session 认证中最常用的一种方式&#xff0c;也是最简单的。存在多节点session丢失的情况&#xff0c;可通过nginx粘性Cookie和Redis集中式Session存储解决 ●HTTP Basic Authentication 服务端针对请求头中base64加密的Authorization 和用户名和密码进行校验。…

分层解耦-03.IOCDI-入门

一. IOC&DI入门 二.控制转移注解Component 因为dao和service接口的实现类对象需要传入到service和controller中&#xff0c;因此需要将dao和service代码加上Component注解&#xff0c;使之实现控制反转&#xff0c;将实现类对象交给IOC容器管理&#xff0c;成为IOC容器中…

字符编码发展史5 — UTF-16和UTF-32

上一篇《字符编码发展史4 — Unicode与UTF-8》我们讲解了Unicode字符集与UTF-8编码。本篇我们将继续讲解字符编码的第三个发展阶段中的UTF-16和UTF-32。 2.3. 第三个阶段 国际化 2.3.2. Unicode的编码方式 2.3.2.2. UTF-16 UTF-16也是一种变长编码&#xff0c;对于一个Unic…

构建快速应用,国内低代码开发平台的选择指南

本文盘点10款主流低代码开发平台&#xff0c;包括ZohoCreator、阿里宜搭等&#xff0c;分析其特点及应用场景。各平台各具优势&#xff0c;适用于不同企业和业务需求&#xff0c;建议企业根据自身需求和技术水平试用后选择。 一、Zoho Creator Zoho Creator 是一个低代码开发平…