数据结构之二叉树由浅入深最终章!

题外话

我说清明节想放松一下没更新大家信吗?

博客毕竟是文字不是视频,大家如果有不明白的地方,可以使用数形结合的方式,画图一边通过图片,一边通过对照代码进行推导一下,有什么问题都可以私信我或者写在评论区

正题

第一题

寻找二叉树中p,q最近公共祖先

第一题思路

先分析有几种情况

1.p,q在二叉树root的两侧

分别寻找p,q如果都找到了则说明在两侧,root就是最近公共祖先直接返回root即可

2.p,q有一个在根节点root

直接返回根节点

3.p,q在同一侧

先找到p或q直接返回,再继续遍历,如果只找到一个p或q,说明找到的是最近公共祖先

第一题代码及详解

public TreeNode  lowestCommonAncestor(TreeNode root,TreeNode p,TreeNode q){
    //判断root是不是空,空直接返回
    if (root==null)
    {
        return root;
    }
    //如果root找到了p或者q直接返回root,继续遍历,找剩下的结点
    if (root==p||root==q)
    {
        return root;
    }
    //从左子树和右子树一起遍历
    TreeNode left=lowestCommonAncestor(root.left,p,q);
    TreeNode right=lowestCommonAncestor(root.right,p,q);
    //如果p或q有一个没有找到,left或right就会返回空,两个都找到了都不为空,证明p,q在二叉树两边
    if (left!=null&&right!=null)
    {
        //两个都不为空则说明在两边,根节点就是最近公共祖先,直接返回根节点即可
        return root;
    }
    //如果left不为空,right为空则说明left就是最近公共祖先
    else if (left!=null&&right==null){
        return left;

    }
    //否则left为空,right不为空,说明right为最近公共祖先
    else {
       return right;
    }

}

第二题

从前序遍历与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

第二题思路

只需要从先序遍历数组中一个一个找到中序遍历对应的元素从根,左,右创建二叉树即可

第二题代码详解

class Solution {

//创建一个全局变量index,让先序遍历数组每个元素都找到中序遍历数组对应的元素

     int index=0;

 创建方法buildTree,传入先序遍历数组和中序遍历数组

    public TreeNode buildTree(int[] preorder, int[] inorder)

    {

        return buildTreeChilde(preorder,inorder,0,inorder.length-1);

    }

//创建方法buildTreeChilde,传入先序遍历数组,中序遍历数组,中序遍历开始下标,和中序遍历最后一个元素下标

    private TreeNode buildTreeChilde(int[] preorder,int[] inorder,int inbegin,int inend)

    {

//如果inbegin大于inend说明遍历完毕

        if (inbegin>inend)

        {

            return null;

        }

//建立对象root用于创建连接二叉树

        TreeNode root=new TreeNode(preorder[index]);

//创建rootIndex接收找到对应先序遍历数组元素对应中序遍历数组元素下标

        int rootIndex=findIndexRoot(inorder,inbegin,inend,preorder[index]);

//如果下标为-1则说明没找到(但基本上不可能找不到)

        if (rootIndex==-1)

        {

            return null;

        }

//找到之后,继续往后创建下一个元素,index++

        index++;

//将找到元素前一个作为左子树(因为中序遍历是左,根,右,根结点前一个元素就是左子树)

        root.left=buildTreeChilde(preorder,inorder,inbegin,rootIndex-1);

//将找到元素后一个作为右子树

        root.right=buildTreeChilde(preorder,inorder,rootIndex+1,inend);

        return root;

    }

//寻找先序遍历数组元素对应的中序遍历数组下标

    private int findIndexRoot(int[] inorder,int inbegin,int inend,int key )

    {

        for (int i = 0; i <= inend; i++) {

            if (inorder[i]==key)

            {

                return i;

            }

        }

//没找到返回-1

        return -1;

    }

}

第三题

根据中序和后序遍历返回二叉树

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

第三题思路

根据后序遍历,左右根,从最后往前在中序遍历中找到对应元素,先建立根节点再建立右子树最后建立左子树,因为遍历的时候是左右根,创建的时候顺序为根有左的顺序

第三题代码详解

class Solution {

//创建一个全局变量postIndex,让后序遍历数组每个元素都找到中序遍历数组对应的元素

       int postIndex;

 //创建方法buildTree,传入后序遍历数组和中序遍历数组

 public TreeNode buildTree( int[] inorder,int[] postorder)

{

//让postIndex为后序遍历最后一个下标

postIndex=postorder.length-1;

return buildTreeChilde(postorder,inorder,0,inorder.length-1);

}

//创建方法buildTreeChilde,传入后序遍历数组,中序遍历数组,中序遍历开始下标,和中序遍历最后一个元素下标

private TreeNode buildTreeChilde(int[] postorder,int[] inorder,int inbegin,int inend)

{

//如果inbegin大于inend说明遍历完毕

if (inbegin>inend)

{

return null;

}

//建立对象root用于创建连接二叉树

TreeNode root=new TreeNode(postorder[postIndex]);

//创建rootIndex接收找到对应后 序遍历数组元素对应中序遍历数组元素下标

int rootIndex=findIndexRoot(inorder,inbegin,inend,postorder[postIndex]);

//如果下标为-1则说明没找到(但基本上不可能找不到)

if (rootIndex==-1)

{

return null;

}

//找到之后,继续往前创建下一个元素,postIndex--

postIndex--;

//将找到元素后一个作为右子树

root.right=buildTreeChilde(postorder,inorder,rootIndex+1,inend);

//将找到元素前一个作为左子树

root.left=buildTreeChilde(postorder,inorder,inbegin,rootIndex-1);

return root;

}

//寻找后序遍历数组元素对应的中序遍历数组下标

private int findIndexRoot(int[] inorder,int inbegin,int inend,int key )

{

for (int i = 0; i <= inend; i++) {

if (inorder[i]==key)

{

return i;

}

}

//没找到返回-1

return -1;

}

}

第四题

根据二叉树创建字符串

给你二叉树的根节点 root ,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。

空节点使用一对空括号对 "()" 表示,转化后需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。

第四题思路

这道题主要是看这两个例子,根据这两个例子咱们分析一下

1.用前序遍历思路

2.根结点直接放入StringBuilder中

3.如果左子树或者右子树不为空,直接在StringBuilder中添加"("

4.左子树不为空,就继续遍历左子树,在StringBuilder中添加")"

5.左子树为空,右子树不为空在StringBuilder中添加"()",左右子树都为空直接返回

6.右子树不为空,就继续遍历右子树,在StringBuilder中添加")"

7.右子树为空,直接返回

第四题代码

//创建全局变量 StringBuilder s

 StringBuilder s=new StringBuilder();

     public String tree2str(TreeNode root) {

//调用  tree2strChild()方法

          tree2strChild(root);

//返回s.toString方法

        return s.toString();

    }

    public void tree2strChild(TreeNode root) {

        //当结点为空,直接返回

        if(root==null)

        {

           return;

        }

        //结点不为空直接将val添加s中

        s.append(root.val);

        //如果结点左子树不为空

        if (root.left!=null)

        {

            //先添加左括号

            s.append("(");

            //递归遍历左子树

            tree2strChild(root.left);

            //添加右括号

            s.append(")");

        }

        else {

            //如果左子树为空,右子树不为空

            if (root.right!=null)

            {

                //添加一对小括号

                s.append("()");

            }

            //如果左子树为空,右子树也为空

            else {

                return;

            }

        }

        //如果右子树不为空

        if(root.right!=null)

        {

            //先添加左括号

            s.append("(");

            //再递归遍历右子树

            tree2strChild(root.right);

            //再添加右括号;

            s.append(")");

        }

        else {

            return;

        }

    }

第五题 

二叉树非递归前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

第五题思路

不用递归前序遍历,我们需要借助别的方法,之前层序遍历我们用的队列,因为队列是先进先出原则

而这里我们用队列无法实现前序遍历(大家可以自己推导一下,如果用队列什么时候出队,什么时候打印结点,就会发现有矛盾的地方)

这里我们用的是栈来实现前序遍历,先进后出原则,我们需要思考以下的问题

1.如何才能实现像递归一样,根左子树右子树这样一直遍历

2.什么时候打印

3.什么时候出栈

1.首先,我们可以用while从根左子树右子树循环,但是只依靠一个while无法实现多重循环,我们要使用两个while,才可以实现像递归一样

大家可以思考一下,如果把一个方法放入while中,意味着只要满足while条件,就会一直循环这个方法

那么这就可以实现类似递归的操作了

2.打印的时机有两个,一个是进栈,一个是出栈,栈遵循先进后出

我们以根,左,右的顺序进栈,但是出栈绝对是右子树会比左子树先出栈,所以出栈打印结点无法实现前序遍历

那么进栈的时候我们就可以边进栈边打印结点

3.我们要先进栈,当左子树遍历完成之后,然后才能出栈,出栈的时候要把出栈结点保存,从而找到它的右子树

第五题代码详解

public void preorderTraversal(TreeNode root) {
//创建栈s
    Stack<TreeNode> s=new Stack<>();
//当结点为空直接返回
    if (root==null)
    {
        return;
    }
//创建临时变量cur,把root赋值给cur
    TreeNode cur=root;
//当cur不为空或者栈中元素没有全部出栈(不为空)
    while (cur!=null||!s.empty()) {
//只要cur不为空
         while (cur!=null) {
//将结点放入栈中,并打印该结点
              s.push(cur);
              System.out.println(cur.val+" ");
//让结点等于其左子树,继续循环
              cur=cur.left;
          }
//当cur为空了,也就是左子树为空了,该遍历右子树了,将栈顶元素,也就是根结点出栈,并指向其右子树
        cur=s.pop();
         cur=cur.right;
    }
}

第六题

给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。

非递归中序遍历

第六题思路

和第五题差不太多

区别就是打印时机和出栈时机

中序遍历是左根右,我们应该把根的左子树都遍历完成,然后再出栈最后一个左子树(先进后出)并保存,然后找其右子树,循环往复即可

第六题代码详解

public void inorderTraversal(TreeNode root) {
//创建栈s
    Stack<TreeNode> s=new Stack<>();
//如果结点为空直接返回
    if (root==null)
    {
        return;
    }
//创建临时变量cur接收root
    TreeNode cur=root;
//当cur不为空或者栈不为空时
    while (cur!=null||!s.empty()) {
//当cur不为空
        while (cur!=null) {
//先入栈
            s.push(cur);
//赋值结点左子树循环往复
            cur=cur.left;
        }
//出栈最后入栈结点(先进后出)并保存出栈结点
        cur=s.pop();
//打印出栈结点值
        System.out.println(cur.val+" ");
//让cur等于其右子树,再次循环
        cur=cur.right;
    }
}

第七题

非递归后序遍历

第七题思路

和五六题差不多,但是也有区别

1.后序遍历,左右根的顺序打印

2.根节点压栈

先循环遍历左子树,当左子树为空了,弹出最后一个左子树并保存,并打印,然后判断其是否存在右子树,存在则压栈继续循环这个过程

第七题代码详解

public void PostOrderNor(TreeNode root)

{

//如果结点为空直接返回

if (root==null)

{

return;

}

//创建栈stack

Stack<TreeNode> stack=new Stack<>();

//把root赋给cur

TreeNode cur=root;

//再创建prev记录最近弹出元素

TreeNode prev=null;

//结点不为空,或者栈不为空的话

while(cur!=null||!stack.empty()) {

//结点不为空

while (cur!=null) {

//cur压栈

stack.push(cur);

//cur先遍历左子树,方便先弹出左子树

cur=cur.left;

}

//建立临时变量top查看栈顶元素

TreeNode top=stack.peek();

//如果栈顶元素右子树等于空或者栈顶元素右子树等于最近弹出元素prev

if (top.right==null||top.right==prev)

{

//打印栈顶元素值

System.out.print(top.val+" ");

//弹出栈顶元素

stack.pop();

//让prev等于最近弹出的元素

prev=top;

}

else {

//如果右子树不为空,或者栈顶元素右子树不等于最近弹出元素prev时,直接遍历右子树 cur=top.right;

}

}

}

小结

最近在宿舍斗志不高,已经开始在听励志歌曲了,我也想逆风翻盘

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++】红黑树讲解及实现

前言&#xff1a; AVL树与红黑树相似&#xff0c;都是一种平衡二叉搜索树&#xff0c;但是AVL树的平衡要求太严格&#xff0c;如果要对AVL树做一些结构修改的操作性能会非常低下&#xff0c;比如&#xff1a;插入时要维护其绝对平衡&#xff0c;旋转的次数比较多&#xff0c;更…

【Claude 3】This organization has been disabled.此组织已被禁用。(Claude无法对话的原因和解决办法)

Claude对话提示 This organization has been disabled.此组织已被禁用。 This organization has been disabled.此组织已被禁用。 This organization has been disabled.此组织已被禁用。 问题截图 问题原因 出现该页面&#xff0c;表示您的账户已经无法使用&#xff0c;可能…

顺序表相关习题

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文…

Jenkins (三) - 拉取编译

Jenkins (三) - 拉取编译 通过Jenkins平台 git 拉取github上项目&#xff0c;通过maven编译并打包。 Jenkins 安装 git 插件 Manager Jenkins -> Plugins -> Available plugins -> Git 打包编译检验 FressStyle 风格编译 New Item输入 item name Spring-Cloud-1…

回溯法(一)——全排列 全组合 子集问题

全排列问题 数字序列 [ l , r ] [l,r] [l,r]​区间内元素的全排列问题 extern int ans[],l,r,num;//num&#xff1a;方案数 extern bool flag[]; void dfs(int cl){//cl:current left&#xff0c;即为当前递归轮的首元素if(cl r 1){//数组已越界&#xff0c;本轮递归结束for…

IDEA2023创建SpringMVC项目

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 开发环境篇 ✨特色专栏&#xff1a; M…

SpringBoot整合Spring Data JPA

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉🍎个人主页:Leo的博客💞当前专栏: 循序渐进学SpringBoot ✨特色专栏: MySQL学习 🥭本文内容: SpringBoot整合Spring Data JPA 📚个人知识库: Leo知识库,欢迎大家访问 1.…

智慧牧场数据 7

1 体征数据采集 需求:获取奶牛记步信息 三轴加速度测量&#xff1a;加速度测量计反应的加速向量与当前的受力方向是相反&#xff0c;单位为g 陀螺仪&#xff0c;是用来测量角速度的&#xff0c;单位为度每秒&#xff08;deg/s&#xff09; 2000deg/s 相当于1秒钟多少转 1.1…

Vue关键知识点

watch侦听器 Vue.js 中的侦听器&#xff08;Watcher&#xff09;是 Vue提供的一种响应式系统的核心机制之一。 监听数据的变化&#xff0c;并在数据发生变化时执行相应的回调函数。 目的:数据变化能够自动更新到视图中 原理&#xff1a; Vue 的侦听器通过观察对象的属性&#…

正排索引 vs 倒排索引 - 搜索引擎具体原理

阅读导航 一、正排索引1. 概念2. 实例 二、倒排索引1. 概念2. 实例 三、正排 VS 倒排1. 正排索引优缺点2. 倒排索引优缺点3. 应用场景 三、搜索引擎原理1. 宏观原理2. 具体原理 一、正排索引 1. 概念 正排索引是一种索引机制&#xff0c;它将文档或数据记录按照某种特定的顺序…

分布式锁实战

4、分布式锁 4.1 、基本原理和实现方式对比 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁&#xff0c;只要大家使用的是同一把锁&#xff0c;那么我们就能锁住线程&#xff0c;不让线程进行&#x…

【鲜货】企业数据治理的首要一步:数据溯源

目录 背景 一、数据探索溯源的定义 二、数据探索溯源的重要性 1、提高数据质量 2、增强数据信任度 3、促进数据合规性 三、数据溯源的主要方法 1、标注法 2、反向查询法 3、双向指针追踪法 四、数据探索溯源的主要步骤 1、确定溯源目标 2、收集元数据 3、分析数据…

微信小程序uniapp+vue.js旅游攻略系统9krxx

实现了一个完整的旅游攻略小程序系统&#xff0c;其中主要有用户模块、用户表模块、token表模块、收藏表模块、视频信息模块、视频类型模块、景点资讯模块、门票购买模块、旅游攻略模块、景点信息模块、论坛表模块、视频信息评论表模块、旅游攻略评论表模块、景点信息评论表模块…

噪声的力量:重新定义 RAG 系统的检索

该文得到了一个反常识的结论&#xff0c;当无关的噪声文档放在正确的位置时&#xff0c;实际上有助于提高RAG的准确性。 摘要 检索增强生成&#xff08;RAG&#xff09;系统代表了传统大语言模型&#xff08;大语言模型&#xff09;的显着进步。 RAG系统通过整合通过信息检索…

CSS基础:插入CSS样式的3种方法

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。大专生&#xff0c;2年时间从1800到月入过万&#xff0c;工作5年买房。 分享成长心得。 262篇原创内容-公众号 后台回复“前端工具”可获取开发工具&#xff0c;持续更新中 后台回复“前端基础…

【UnityRPG游戏制作】Unity_RPG项目之界面面板分离和搭建

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

2_5.Linux存储的基本管理

实验环境&#xff1a; 系统里添加两块硬盘 ##1.设备识别## 设备接入系统后都是以文件的形式存在 设备文件名称&#xff1a; SATA/SAS/USB /dev/sda,/dev/sdb ##s SATA, dDISK a第几块 IDE /dev/hd0,/dev/hd1 ##h hard VIRTIO-BLOCK /de…

stm32开发之threadx使用记录(主逻辑分析)

前言 threadx的相关参考资料 论坛资料、微软官网本次使用的开发板为普中科技–麒麟&#xff0c;核心芯片为 stm32f497zgt6开发工具选择的是stm32cubemx(代码生成工具)clion(代码编写工具)编译构建环境选择的是arm-none-gcc编译 本次项目结构 CMakeList对应的配置 set(CMAKE_…

Seata(分布式事务集成测试和总结)

文章目录 1.集成测试1.集成测试正常下单1.步骤2.浏览器访问 http://localhost:10008/order/save?userId666&productId1&nums1&money1003.注意事项和细节 2.集成测试模拟异常1.步骤1.com/sun/springcloud/controller/StorageController.java 休眠12s&#xff0c;模…

虚拟机打不开

问题 另一个程序已锁定文件的一部分&#xff0c;进程无法访问 打不开磁盘“G:\centeros\hadoop104kl\hadoop100-cl2.vmdk”或它所依赖的某个快照磁盘。 模块“Disk”启动失败。 未能启动虚拟机。 原因 前一次非正常关闭虚拟机导致.lck 文件是VMWare软件的一种磁盘锁文件&…