二叉树OJ题进阶(二叉树层序遍历、根据二叉树创建字符串、判断完全二叉树、二叉树的构建及遍历、二叉树的最近公共祖先(2种))

news2024/11/30 12:27:06

文章目录

  • 二叉树OJ进阶
    • 一、 二叉树层序遍历
      • 1.思路
      • 2.代码
    • 二、根据二叉树创建字符串
      • 1.思路
      • 2.代码
    • 三、判断完全二叉树
      • 1.思路
      • 2.代码
    • 四、二叉树的构建及遍历
      • 1.思路
      • 2.代码
    • 五、二叉树的最近公共祖先
      • 方法一:思路
      • 代码
      • 方法二:思路
      • 代码


二叉树OJ进阶


一、 二叉树层序遍历

在这里插入图片描述

1.思路

用队列写:

1.从上到下,从左到右的顺序

2.非递归的方法:使用队列来完成

3.cur充当根结点,当cur不为空的时候,cur进入队列,队列不为空,cur弹出队列打印

4.如果cur的左边不为空,左边进队,右边不为空,右边进队

5.此时队列不为空,弹出队头(也就是cur的左边)打印,cur移动到cur的左边

6.也就是说,如果队列不为空,弹出一个队头元素并打印,弹出的元素的左右结点如果不为空,进队

7.先入再入右,保证从左到右打印,也就是说,利用队列先进先出(挤牙膏)的特性,先入队的是当前根结点的左右结点,然后是结点的左右结点,保证了从上到下打印,打印当前根结点的时候,根结点的左右结点进队

8.也就是说,在打印上层的每个结点时,每个结点的下一层元素按顺序进队,循环往复

9.当cur为叶子结点的时候,叶子结点的左右结点为空,不入队,只出队,当队列为空时,打印完毕

2.代码

    public List<List<Integer>> levelOrder2(TreeNode root) {
        List<List<Integer>> list = new ArrayList<>();
        if (root == null) {//根节点如果是0,返回空
            return list;
        }
        Queue<TreeNode> queue = new LinkedList<>();//实例化一个队列
        queue.offer(root);//根结点进队
        while (!queue.isEmpty()) {//队列不为空时
            int size = queue.size();
            List<Integer> tmp = new ArrayList<>();
            while (size != 0) {
                TreeNode cur = queue.poll();//取出之前进队的根节点,并让cur指向该结点
               // System.out.print(cur.val + " ");//取出来就打印
                tmp.add(cur.val);//取出来的值不打印,直接存进tmp
                size--;
                if (cur.left != null) {//该结点的左右子树如果不为空,进队
                    queue.offer(cur.left);
                }
                if (cur.right != null) {
                    queue.offer(cur.right);
                }
            }
            list.add(tmp);
        }
        return list;
    }

二、根据二叉树创建字符串

在这里插入图片描述

1.思路

1.前序遍历,用StringBuilder拼接字符串

2.放进根节点,如果子结点不为空 + “( ”

3.左右子树如果都为空,直接返回

4.子树走完了,+ “ )”

5.左边为空,右边不为空 + “()”

  • 也就是说,当结点的左子树不为空时,先拼接当前结点,然后+( ,进入左子树的递归
  • 当出现左子树为空,右子树不为空的情况时,+()
  • 左子树递归完了+),判断右子树,右子树为空返回,不为空,+( ,进入右子树的递归

2.代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public String tree2str(TreeNode root) {
        if (root == null) {
            return null;
        }
        StringBuilder stringBuilder = new StringBuilder();
        tree2strChild(root, stringBuilder);
        return stringBuilder.toString();

    }

    public void tree2strChild(TreeNode root, StringBuilder sb ){
        if (root == null){//root等于空,直接返回
            return;
        }
        sb.append(root.val);//拼接root的值
        if (root.left!=null){//左边不为空的情况
            sb.append('(');
            tree2strChild(root.left,sb);//进入左子树判断
            sb.append(')');//左子树完成后+)
        }else {
            //左边为空
            if (root.right!=null){//左边为空,右边不为空
                sb.append("()");
            }else {
               return;//左右都为空,直接返回
            }
        }
        if (root.right!=null){//右边不为空的情况
            sb.append('(');
            tree2strChild(root.right,sb);
            sb.append(')');//右子树完成后+)
        }else {
            return;
        }
    }
}

三、判断完全二叉树

判断一棵树是不是完全二叉树

1.思路

1 .和层序遍历类型,用队列来完成

2 .如果根节点不为空,根节点入队

3.判断队列如果不为空,弹出队头并用cur引用(用cur指向该结点)

4.将cur的左有子结点进队,不论空不空

5.进入循环,队列不为空,弹出当前的头结点。cur的左右子节点进队(不管空不空)

6.当队列中弹出的元素为空的时候,cur指向空,所以结点都遍历完毕,循环结束

7.此时,当队列不为空时进行循环,依次取出队列中的元素,如果出现不为空的元素,说明不是完全二叉树

8.如果队列中都是空,说明是完全二叉树

  • 因为完全二叉树就是从上到下,从左到右依次排列,队列中的元素按层序排序依次放加入
  • 如果出现加杂着null的情况,证明不是完全二叉树

2.代码

    // 判断一棵树是不是完全二叉树
   public boolean isCompleteTree(TreeNode root){
        if (root == null){
            return true;
        }//尽量接口引用对象
       Deque<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()){//队列不为空
            TreeNode cur = queue.poll();//弹出对队头
            if (cur != null){//cur不为空,左右子节点进队
                queue.offer(cur.left);
                queue.offer(cur.right);
            }else{
                break;//cur为空,结束循环
            }
        }
        while (!queue.isEmpty()){
            TreeNode tmp = queue.poll();//依次出队
            if (tmp != null){
                return false;
            }
        }
        return true;
   }

四、二叉树的构建及遍历

在这里插入图片描述

1.思路

在这里插入图片描述

1.根据前序遍历的字符串,创建二叉树,再通过创建的二叉树,打印中序遍历

2.给了前序遍历,可以找到根节点的位置

3.#代表空树,空节点是指定的,因此可以创建唯一二叉树

4.前序遍历,先创建根节点,如果遇到#,返回的同时用子节点接收

5.因为要递归,所以不采用循环来遍历字符串,设置一个静态成员变量i

6.读取字符串中i位置的字符,如果不是#,根据取到的值创建根节点,i++;

7.先创建左子树,再创建右子树,根节点为空时返回,根节点的左右分别指向返回值

8.如果遇到#,i++,返回的结点为null

9.利用中序遍历打印

2.代码

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (in.hasNextLine()) { // 
           String str = in.nextLine();
           TreeNode root = creatTree(str);
           inorder(root);
        }
    }
    public static int i = 0;//i记录读取的字符
    public static TreeNode creatTree(String str){//创建二叉树
        TreeNode root = null;
       if(str.charAt(i)!='#'){
        root = new TreeNode(str.charAt(i));
        i++;//创建完根节点后,i++,先创建左树,再创建右树
        root.left = creatTree(str);
        root.right = creatTree(str);
       }else{
        i++;//如果是#,i++跳过
       }
        return root;//返回root,i是#时,返回空,i不是#时,返回结点,让root指向对应结点
    }
    public static void inorder(TreeNode root){
        if(root==null){//中序遍历打印
            return;
        }
        inorder(root.left);
        System.out.print(root.val+" ");
        inorder(root.right);
    }
}
  • i 会不会越界?
  • 不会,递归的次数严格执行给的前序遍历,除非给的字符串非法,否则i不会越界

五、二叉树的最近公共祖先

在这里插入图片描述

方法一:思路

方法一:

1.p、q可能在根的左右两边

2.要么都在根的左边或者右边

3.要么其中一个结点是公共祖先

4.先判断如果p、q是根节点,则根节点就是公共祖先

5.递归,分别在左右两边去找p、q

6.如果左右两边递归回来的值都不为空,说明两边都找到了,根结点就是公共祖先,返回根节点

7.因为pq一定在树中,如果左右返回的值,一个为空,一个不为空,说明找到的值就是pq的公共祖先

8.如果左右都为空,没找到,返回null

代码

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null ){//判断是否为空,返回空
            return null;
        }
        if(root == q || root == p){//判断根节点是不是公共祖先
            return root;
        }
        TreeNode leftRet = lowestCommonAncestor(root.left,p,q);//在根的左边找pq 
        TreeNode rightRet = lowestCommonAncestor(root.right,p,q);//在跟的右边找pq

        if(leftRet != null && rightRet != null){//左右都不空
            return root;//如果左右两个分别都找到了,根节点就是公共祖先
        }else if(leftRet != null){//左边不为空,右边为空
        //因为pq存在于树中,所以找到的结点就是pq的公共祖先
            return leftRet;
        }else if(rightRet != null){//右边不为空,左边为空
            return rightRet;
        }
        return null;//左右都为空,返回空
    }

方法二:思路

如果给每一个结点加一个父亲结点,每一个结点都有父亲结点的地址,就变成了求相交结点

也可以用栈来完成:

  • 因为栈是先进后出的,没有父亲结点,用栈来存储路径的上一个结点

1.用两个栈来存从根节点到p和q的路径上遇到的左右结点

2.两个栈,判断栈的大小,谁多谁先弹出一个,然后再同时弹出

3.将弹出的结点进行比较,值不一样,不是公共祖先,如果弹出的两个一样,则返回找到的结点

4.栈出空了,没有公共祖先,返回null

  • 怎么找到路径,存放进栈(如何确保,栈中存的就是p、q的正确路径)

    1.根不为空,直接压栈,判断根节点是不是要找到结点

    2.根节点不是,递归:从左边开始找,左边没找到,再去右边找

    2.左右两边都没找到要找的结点,弹出压进的结点,返回false

代码

    public boolean getPath(TreeNode root, TreeNode node, Deque<TreeNode> stack) {//
        if (root == null || node == null) {
            return false;
        }
        stack.push(root);//根节点压栈
        //放完后检查,
        if (root == node) {
            return true;
        }//根节点不是,从左边开始找
        boolean ret1 = getPath(root.left, node, stack);
        if (ret1 == true) {
            return true;
        }//左边没找到,再去右边找
        boolean ret2 = getPath(root.right, node, stack);
        if (ret2 == true) {
            return true;
        }
        stack.pop();//左右都没找到弹出压进的结点
        return false;
    }

    public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) {
        //在两个栈当中,存好对应的路径
        Deque<TreeNode> stack1 = new ArrayDeque<>();
        getPath(root, p, stack1);
        Deque<TreeNode> stack2 = new ArrayDeque<>();
        getPath(root, q, stack2);

        //判断栈的大小
        int size1 = stack1.size();
        int size2 = stack2.size();
        if (size1 > size2) {
            int size = size1 - size2;
            while (size != 0) {
                stack1.pop();
                size--;
            }
        } else {
            int size = size2 - size1;
            while (size2 != 0) {
                stack2.pop();
                size--;
            }
        }//栈里面结点的个数一样了
        while (!stack1.isEmpty()&&!stack2.isEmpty()){
            //如果两个栈都不为空,看弹出的值一不一样
            if (stack1.peek()!=stack2.peek()){//不相等,弹出
                stack1.pop();
                stack2.pop();
            }else {
                return stack1.peek();

            }
        }
        return null;//栈出空了,没有公共祖先,返回空
    }

点击移步博客主页,欢迎光临~

偷cyk的图

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

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

相关文章

线性代数 第五章 特征值与特征向量

一、特征值定义 二、特征值求法 定义法&#xff1b;&#xff1b;相似。 三、特征向量求法 定义法&#xff1b;基础解系法&#xff1b;&#xff1b;相似。 四、特征值性质 不同特征值的特征向量线性无关k重特征值至多有k个线性无关的特征向量 五、相似的定义 若&#xff…

企业邀约媒体的方式方法?-(快速精准)

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 快速而精确地邀约媒体通常需要有计划和策略性的方法。以下是一些方法&#xff0c;可以帮助企业有效地邀请媒体&#xff1a; 1. 媒体列表构建&#xff1a;首先&#xff0c;建立一个精心筛…

JavaScript从入门到精通系列第二十八篇:详解JavaScript中的字符串的方法

大神链接&#xff1a;作者有幸结识技术大神孙哥为好友&#xff0c;获益匪浅。现在把孙哥视频分享给大家。 孙哥链接&#xff1a;孙哥个人主页 作者简介&#xff1a;一个颜值99分&#xff0c;只比孙哥差一点的程序员 本专栏简介&#xff1a;话不多说&#xff0c;让我们一起干翻J…

Zabbix技术分享——使用SNMPv2监控网络设备

前言&#xff1a;SNMP介绍 SNMP&#xff08;简单网关协议&#xff0c;Simple Network Management Protocol&#xff09;是专门设计用于在 IP 网络管理网络节点&#xff08;服务器、工作站、路由器、交换机及HUBS等&#xff09;的一种标准协议&#xff0c;它是一种应用层协议。 …

unraid 安装并设置 zerotier 内网穿透安装 unraid 局域网内其他设备

Read Original 最近看了以下两个文章&#xff0c;感谢发布的各种精彩文章&#xff0c;让我受益匪浅。OPENWRT 的固件在设置了&#xff0c;【自动允许客户端 NAT】后&#xff0c;可以直接访问局域网其他设备&#xff0c;而我 unraid 部署 zerotier 后&#xff0c;只能访问 unra…

全球高分辨率地表太阳辐射数据集包含36年(1983.7-2018.12)

简介&#xff1a; 全球高分辨率地表太阳辐射数据集包含36年&#xff08;1983.7-2018.12&#xff09;的全球地表太阳辐射数据&#xff0c;其分辨率为3小时&#xff0c;10公里&#xff0c;数据单位为W/㎡&#xff0c;瞬时值。该数据集可用于水文建模、地表建模和工程应用&#x…

Zephyr-7B-β :类GPT的高速推理LLM

Zephyr 是一系列语言模型&#xff0c;经过训练可以充当有用的助手。 Zephyr-7B-β 是该系列中的第二个模型&#xff0c;是 Mistralai/Mistral-7B-v0.1 的微调版本&#xff0c;使用直接偏好优化 (DPO) 在公开可用的合成数据集上进行训练 。 我们发现&#xff0c;删除这些数据集的…

GD32 单片机 硬件I2C死锁解决方法

死锁的复现方式 在I2C恢复函数下个断点&#xff08;检测到I2C多次超时之后&#xff0c;应该能跳转到I2C恢复函数&#xff09;使用镊子&#xff0c;将SCL与SDA短接&#xff0c;很快就能看到程序停到恢复函数的断点上&#xff0c;此时再执行恢复函数&#xff0c;看能否正常走出&…

Redis系列之Redis入门级(带你初步认识Redis)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《LInux实战开发》。&#x1f3af;&#x1f3af; …

物联网云端管理软件 IoTstar 3.5.1

IoTstar 是为各种工业物联网应用中的WISE/PMC/PMD 控制器开发的软件。IoTstar可以安装在通用PC平台上作为私有物联网云系统&#xff0c;也可以安装在Microsoft Azure、IBM Bluemix、Google Cloud或Amazon AWS等VM&#xff08;虚拟机&#xff09;平台上作为公共物联网云系统。 I…

Pymysql模块使用操作

一、pymysql模块安装 二、测试数据库连接 测试数据库连接.py from pymysql import Connectioncon None try:# 创建数据库连接con Connection(host"localhost",port3306,user"root",password"XXXXX")# 测试链接print(con.get_host_info())print…

面试10000次依然会问的【volatile】,你还不会?

volatile关键字的定义 volatile是Java语言提供的一种轻量级的同步机制&#xff0c;主要用于确保变量的修改对其他线程是立即可见的&#xff0c;以及防止指令重排序。使用volatile修饰的变量&#xff0c;其读写操作直接作用于主存&#xff0c;而不是线程的工作内存。 这意味着…

ubuntu 20.04 + cuda-11.8 + cudnn-8.6+TensorRT-8.6

1、装显卡驱动 ubuntu20.04 cuda10.0 cudnn7.6.4_我是谁&#xff1f;&#xff1f;的博客-CSDN博客 查看支持的驱动版本&#xff1a; 查看本机显卡能够配置的驱动信息 luhost:/usr/local$ ubuntu-drivers devices/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0 moda…

2023-11-03 LeetCode每日一题(填充每个节点的下一个右侧节点指针 II)

2023-11-03每日一题 一、题目编号 117. 填充每个节点的下一个右侧节点指针 II二、题目链接 点击跳转到题目位置 三、题目描述 给定一个二叉树&#xff1a; struct Node { int val; Node *left; Node *right; Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针…

Windows 11 Home 中启用 Hyper-V

Hyper-V 是微软开发的基于硬件的虚拟机管理程序。它允许用户在 Windows 操作系统之上运行不同操作系统的多个实例。目前&#xff0c;Hyper-V 也支持 Windows、Ubuntu 和其他 Linux 发行版。 如果发现像我这样电脑上启用Hyper-V选项可以按照以下步骤进行操作。 一、新建一个txt…

接上回,如何用 LlamaIndex 搭建聊天机器人?

LlamaIndex 是领先的开源数据检索框架&#xff0c;能够在各种应用中发挥优势&#xff0c;其中一个典型的应用就是在企业内部搭建聊天机器人。 对于企业而言&#xff0c;随着文档数量不断增多&#xff0c;文档管理会变得愈发困难。因此&#xff0c;许多企业会基于内部知识库搭建…

企业办公为什么要选择局域网im即时通讯软件

办公沟通对于企业来说至关重要&#xff0c;而选择局域网IM即时通讯软件作为沟通工具&#xff0c;有以下几个重要原因&#xff1a; 安全性保障&#xff1a;使用局域网IM即时通讯软件&#xff0c;所有的通信数据都在企业内部网络中传输&#xff0c;不会经过公共互联网。这极大地…

C代码内存区域划分

C代码内存区域划分 1、初始化不为零的&#xff08;全局变量、静态全局变量和静态局部变量&#xff09;放在.data段 2、初始化为0&#xff0c;和未初始化的&#xff08;全局变量、静态全局变量和静态局部变量&#xff09;放在.bss 3、编译阶段未初始化的全局变量放在COM块&…

win10、win11解决应用商店、xbox错误代码0x80072efd、0x80131505的方法

文章目录 问题解决方法win10修改方法找到网络和共享中心找到Internet属性点击局域网设置解决后效果 win11的解决方法打开Internet选项找到局域网设置局域网设置 问题 在window上使用win10或者win11自带的系统时&#xff0c;应用商店、xbox报错错误代码0x80072efd、0x80131505。…

【Synopsys工具使用】VCS使用与Makefile脚本调用

文章目录 一、文件导入二、VCS仿真&#xff08;使用可视化界面&#xff09;三、VCS仿真&#xff08;使用Maefile文件&#xff09;3.1 Makefile文件编写3.2 仿真文件编写规范3.3 Makefile文件使用 一、文件导入 新建一个文件夹新建一个文件夹(图中IC_work)   创建一个目录&…