二叉树进阶oj题目

二叉树进阶oj题目

  • 两个结点的最近公共祖先
  • 前序中序(中序后序)还原二叉树

1、两个结点的最近公共祖先(两种方法)

leetcode链接
题目描述:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”如下图所示:
在这里插入图片描述
对于这个问题,有两种方法可以解决。
方法一:分类讨论
思考一下,p和q结点相较于最初的根结点root的位置有哪几种情况?

  • 1、p和q结点中的一个是root结点
  • 2、p和q结点分别在root结点的左右子树中
  • 3、p和q结点都在root结点的一颗子树中

对于情况1,p和q结点的最近公共祖先必然为root,直接返回root即可。
对于情况2,p和q的最近公共祖先也为root,直接返回root即可。
对于情况3,我们需要对于root结点的左孩子和右孩子递归调用这个寻找公共祖先的方法,如果两个方法得到的返回值都不为空,返回当前结点即可。否则,返回非空的那个结点。(不存在两个都为空的情况)。以下是代码的呈现:

public TreeNode lowestCommonAncestor(TreeNode root,TreeNode p,TreeNode q) {
        if(root==null) return null;
        if(q==root) return q;//找到了结点就返回
        if(p==root) return p;
        TreeNode retleft = lowestCommonAncestor(root.left,p,q);//左孩子递归
        TreeNode retright = lowestCommonAncestor(root.right,p,q);//右孩子递归
        if(retleft!=null&&retright!=null) {
            return root;
        }else if(retleft==null) {
            return retright;
        }else {
            return retleft;
        }
}

方法二:记录路径
实际上,我们只要能够**记录下根节点到p结点和q结点的路径,这两个路径事实上是两个链表,这个问题就可以转化成两个链表相交的问题。**由于二叉树的结点并没有parent引用,无法从下往上将路径贯通,那我们该如何来记录路径呢?**我们使用的思路类似于dfs(深度优先搜索),同时使用栈这种数据结构,一条条路径去尝试。**对于非叶子结点,将左孩子结点入栈,不断递归判断是否为p或q,是p或q结点就返回,否则直到叶子节点开始返回,将不在所求路径上的结点出栈,并将右孩子结点放入。在将结点反复地出入栈后,最后栈里留下的就是root到p或q结点的路径。(p和q结点的路径需要分别存放在两个栈中)。
代码如下:

 public boolean getPath(TreeNode root, TreeNode node,
                           Stack<TreeNode> stack) {
        if(root == null) {
            return false;
        }
        stack.push(root);
        if(root == node) {
            return true;
        }
        boolean flgLeft = getPath(root.left,node,stack);
        if(flgLeft) {
            return true;
        }
        boolean flgRight = getPath(root.right,node,stack);
        if(flgRight) {
            return true;
        }
        stack.pop();
        return false;
    }

接下来,就是处理链表相交结点的问题,解决步骤:
1、计算两个栈(链表)的大小之差diff(大-小)
2、将较大的栈(链表)中diff个元素出栈(尾删)
3、当两个栈(链表)不为空时,遍历比较栈顶(链表头)的元素,相同则直接返回,不同则分别出栈(尾删),重复操作直到相同。

2、前序中序(中序后序)还原二叉树

前序中序:leetcode链接
中序后序:leetcode链接
在讲解这道题目前,我们要对二叉树的前序、中序和后序遍历有一个更深刻的了解。
我们先来对前序和中序遍历还原二叉树的过程做一个模拟。比如说我们有一个前序和中序的二叉树遍历序列:
preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
我们要做的就是从前到后遍历preorder,而inorder是用来确定左子树和右子树是由哪些部分构成的。我们来模拟一下这个过程:
1、preorder中第一个结点3,在inorder中3在下标1的位置,3的左边就是左子树,右边就是右子树
2、preorder中第二个结点9,在inorder中9在下标0的位置,9的左边没有数,左树为空,右边本来应该是3,但是3已经访问过了,所以9的右树也为空。

以此类推,我们就可以还原出整棵树。我们在还原过程中要用到四个参数,preorder序列,inorder序列,以及inBegin,inEnd来判断是否这个结点的子树是否还原完成,**初始值分别赋为0和preorder.length-1,**可以通过构建一个函数buildChildTree方法来实现。

  public int preIndex;//定义为静态成员变量,防止受到递归的影响
  public TreeNode buildTree(int[] preorder, int[] inorder) {
        return buildTreeChild(preorder,inorder,0,preorder.length-1);
  }

buildTreeChild方法实现步骤:
1、创建结点

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

2、中序遍历中找到当前结点下标

   int rootIndex = findRooIndex(inorder,inBegin,inEnd,preorder[preIndex]);
   preIndex++
   public int findRooIndex(int[] inoder,int inBegin,int inEnd,int key) {
        for(int i=inBegin;i<=inEnd;i++) {
            if(inoder[i]==key) {
                return i;
            }
        }
        return -1;
    }

3、分别创建左右子树

   root.left = buildTreeChild(preorder,inorder,inBegin,rootIndex-1);
   root.right =buildTreeChild(preorder,inorder,rootIndex+1,inEnd);

4、判断条件:inBegin>inEnd,子树构建完毕,返回null
以结点9为例,root.left调用buildTreeChild方法后,这个方法内部,inBegin的值为0,inEnd的值为-1,左树为null,root.right调用buildTreeChild方法后,这个方法内部inBegin为1,inEnd为preorder.length-1,因为查找不到9,rootIndex返回-1。
下面是完整的代码呈现:

class Solution {
    public int preIndex;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return buildTreeChild(preorder,inorder,0,preorder.length-1);
    }
    public TreeNode buildTreeChild(int[] preorder,int[] inorder,int inBegin,int inEnd) {
        if(inBegin>inEnd) {
            return null;
        }
        //1、创建结点
        TreeNode root = new TreeNode(preorder[preIndex]);
        //2、中序遍历中找到当前结点下标
        int rootIndex = findRooIndex(inorder,inBegin,inEnd,preorder[preIndex]);
        preIndex++;
        //3、分别创建左右子树
        root.left = buildTreeChild(preorder,inorder,inBegin,rootIndex-1);
        root.right =buildTreeChild(preorder,inorder,rootIndex+1,inEnd);
        return root; 
    }
    public int findRooIndex(int[] inoder,int inBegin,int inEnd,int key) {
        for(int i=inBegin;i<=inEnd;i++) {
            if(inoder[i]==key) {
                return i;
            }
        }
        return -1;
    }
}

而对于后序中序还原二叉树,我们也可以按照这种思路模拟实现。唯一需要注意的是我们要从后向前遍历posorder这个序列,同时要先创建右子树,再创建左子树,这样才能符合二叉树后序遍历的顺序。下面直接呈现代码:

class Solution {
    public int postIndex;
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        postIndex = inorder.length-1;
        return buildTreeChild(postorder,inorder,0,inorder.length-1);
    }
    public TreeNode buildTreeChild(int[] postorder,int[] inorder,int inBegin,int inEnd) {
        if(inBegin>inEnd) {
            return null;
        }
        //1、创建结点
        TreeNode root = new TreeNode(postorder[postIndex]);
        //2、中序遍历中找到当前结点下标
        int rootIndex = findRooIndex(inorder,inBegin,inEnd,postorder[postIndex]);
        postIndex--;
        //3、分别创建右左子树
        root.right =buildTreeChild(postorder,inorder,rootIndex+1,inEnd);
        root.left = buildTreeChild(postorder,inorder,inBegin,rootIndex-1);
        return root; 
    }
    public int findRooIndex(int[] inoder,int inBegin,int inEnd,int key) {
        for(int i=inBegin;i<=inEnd;i++) {
            if(inoder[i]==key) {
                return i;
            }
        }
        return -1;
    }
}

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

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

相关文章

【Linux系统编程】环境变量详解

文章目录 1. 环境变量的基本概念2. 如何理解呢&#xff1f;&#xff08;测试PATH&#xff09;2.1 切入点1查看具体的环境变量原因剖析常见环境变量 2.2 切入点2给PATH环境变量添加新路径将我们自己的命令拷贝到PATH已有路径里面 2.3 切入点3 3. 显示所有环境变量4. 测试HOME5. …

【浅谈Linux中批量化注释和批量化去注释】

这篇博客主要是关于Linux中注释与去注释&#xff0c;在Linux和vs等编译器中代码行的注释和去注释会有很大不同&#xff0c;Linux中主要使用指令的方式来进行。 目录 批量化注释 批量化去注释 批量化注释 操作 ctrlv,hjkl区域选择&#xff08;主要使用j-下移&#xff09;&…

GPT应用_PrivateGPT

项目地址&#xff1a;https://github.com/imartinez/privateGPT 1 功能 1.1 整体功能&#xff0c;想解决什么问题 搭建完整的 RAG 系统&#xff0c;与 FastGPT 相比&#xff0c;界面比较简单。但是底层支持比较丰富&#xff0c;可用于知识库的完全本地部署&#xff0c;包含大…

Ubuntu22.04安装GitLab

如果我们是自己本地进行开发,使用Git的简单版本管理功能即可。但如果要做协同开发,使用GitLab自己部署Git代码仓库,是一个不错的选择。 笔者曾使用过svn和Git,相比较而言,Git的使用体验更好。 那么我们接下来安装一下。 安装 首先是升级下包源信息 sudo apt update …

.NET国产化改造探索(六)、银河麒麟操作系统中安装多个.NET版本

随着时代的发展以及近年来信创工作和…废话就不多说了&#xff0c;这个系列就是为.NET遇到国产化需求的一个闭坑系列。接下来&#xff0c;看操作。 上一篇文章介绍了如何在银河麒麟操作系统上&#xff0c;使用Nginx.NET程序实现自启动。本文介绍下如何在一个环境中&#xff0c;…

19.云原生CICD之ArgoCD入门

云原生专栏大纲 文章目录 ArgoCDArgoCD 简介GitOps介绍Argo CD 的工作流程argocd和jinkens对比kustomize介绍ArgoCD和kustomize关系 安装argocdargocd控制台介绍首页应用创建表单SYNC OPTIONS&#xff08;同步选项&#xff09;SYNC POLICY&#xff08;同步策略&#xff09; 应…

以超市数据微案例-fineBI可视化分析

一、入门案例&#xff1a; 2.分析思路&#xff1a; 数据清晰界面中添加毛利额计算 **所以在新增步骤之后&#xff0c;必须点击保存并更新&#xff0c;否则可视化界面中无法使用最新的数据 4、数据可视化分析 1&#xff09;销售额最高的十大商品种类 为1-8月超市数据&#xff…

代码随想录刷题题Day38

刷题的第三十八天&#xff0c;希望自己能够不断坚持下去&#xff0c;迎来蜕变。&#x1f600;&#x1f600;&#x1f600; 刷题语言&#xff1a;C Day38 任务 ● 300.最长递增子序列 ● 674. 最长连续递增序列 ● 718. 最长重复子数组 1 最长递增子序列 300.最长递增子序列 …

基于springboot+vue考编论坛

摘要 近年来&#xff0c;随着互联网的迅猛发展&#xff0c;编程论坛成为程序员们交流学术、分享经验的重要平台之一。为了满足广大程序员的需求&#xff0c;本文基于Spring Boot和Vue框架&#xff0c;设计并实现了一个功能强大的编程论坛。首先&#xff0c;我们选择Spring Boot…

微软使其AI驱动的阅读导师免费

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Excel 根据日期按月汇总公式

Excel 根据日期按月汇总公式 数据透视表日期那一列右击&#xff0c;选择“组合”&#xff0c;步长选择“月” 参考 Excel 根据日期按月汇总公式Excel如何按着日期来做每月求和

怎么提升搜狗网站排名

在当今数字化时代&#xff0c;网站排名对于品牌、企业以及个人都至关重要。而对于许多网站来说&#xff0c;搜狗搜索引擎是一个重要的流量来源。为了在搜狗上取得更好的排名&#xff0c;不仅需要优化网站内容&#xff0c;还需要巧妙运用一些工具和技巧。在本文中&#xff0c;我…

MySQL---单表查询综合练习

创建emp表 CREATE TABLE emp( empno INT(4) NOT NULL COMMENT 员工编号, ename VARCHAR(10) COMMENT 员工名字, job VARCHAR(10) COMMENT 职位, mgr INT(4) COMMENT 上司, hiredate DATE COMMENT 入职时间, sal INT(7) COMMENT 基本工资, comm INT(7) COMMENT 补贴, deptno INT…

人工智能时代的十大核心技术:重塑未来的无限可能 - 引言

在人工智能&#xff08;AI&#xff09;的浪潮中&#xff0c;无数技术如雨后春笋般涌现&#xff0c;引领着人类社会迈向一个崭新的时代。这些技术不仅在理论上具有突破性&#xff0c;更在实际应用中展现出巨大的潜力和价值。 本文将围绕人工智能时代的十大核心技术展开&#xff…

《Linux高性能服务器编程》笔记05

Linux高性能服务器编程 本文是读书笔记&#xff0c;如有侵权&#xff0c;请联系删除。 参考 Linux高性能服务器编程源码: https://github.com/raichen/LinuxServerCodes 豆瓣: Linux高性能服务器编程 文章目录 Linux高性能服务器编程第12章 高性能I/O框架库Libevent12.1 I/…

基于BERT对中文邮件内容分类

用BERT做中文邮件内容分类 项目背景与意义项目思路数据集介绍环境配置数据加载与预处理自定义数据集模型训练加载BERT预训练模型开始训练 预测效果 项目背景与意义 本文是《用BERT做中文邮件内容分类》系列的第二篇&#xff0c;该系列项目持续更新中。系列的起源是《使用Paddl…

采集B站up主视频信息

一、网页信息&#xff08;示例网址&#xff1a;https://space.bilibili.com/3493110839511225/video&#xff09; 二、查看响应数据 三、查看数据包内容 四、相关代码&#xff08;代码内容未进行翻页爬取&#xff09; # Time: 2024/1/19 16:42 # Author: 马龙强 # File: 采集B…

【Linux】第三十二站:命名管道

文章目录 一、命名管道介绍二、编码1.mkfifo2.unlink3.一个简单的例子4.修改 一、命名管道介绍 管道应用的一个限制就是只能在具有共同祖先&#xff08;具有亲缘关系&#xff09;的进程间通信。 如果我们想在不相关的进程之间交换数据&#xff0c;可以使用FIFO文件来做这项工作…

<软考高项备考>《论文专题 - 78 风险管理(10)》

10 论文-历年真题解析 10.1 2005年上半年真题 请围绕“项目的风险管理”论题&#xff0c;分别从以下三个方面进行论述&#xff1a; 1&#xff0e;概要叙述你参与管理过的信息系统项目&#xff08;项目的背景、发起单位、目的、项目周期、交付的产品等&#xff09;&#xff0c…

【排序算法】五、冒泡排序(C/C++)

「前言」文章内容是排序算法之冒泡排序的讲解。&#xff08;所有文章已经分类好&#xff0c;放心食用&#xff09; 「归属专栏」排序算法 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 冒泡排序1.1 原理1.2 代码实现&#xff08;C/C&#xff09;1.3 特性总结 冒泡排序 1.1…