Java数据结构中二叉树的深度解析及常见OJ题

news2024/11/27 11:44:29

本篇文章讲述Java数据结构中关于二叉树相关知识及常见的二叉树OJ题做法讲解(包含非递归遍历二叉树)


目录

一、二叉树

1.1二叉树概念

 1.2特殊的二叉树

 1.3二叉树性质

1.4二叉树基本性质定理题

1.5二叉树遍历基本操作

1.6二叉树遍历的前中后非递归写法

1.7二叉树有关的基本操作

二、二叉树常见的OJ题

2.1相同的树

 2.2对称二叉树

2.3反转二叉树

2.4二叉树的公共祖先

2.5二叉树的层序遍历

2.6根据二叉树前序与中序序列构造二叉树


一、二叉树

1.1二叉树概念

一棵二叉树是结点的一个有限集合,该集合:
1. 或者为空
2. 或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。

3. 二叉树不存在度大于2的结点
4. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

 1.2特殊的二叉树

1. 满二叉树: 一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。也就是说,如果一棵二叉树的层数为K,且结点总数是 ,则它就是满二叉树。

2. 完全二叉树: 完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从0至n-1的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

 1.3二叉树性质

1. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有2^i-1 (i>0)个结点。
2. 若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 2^k-1(k>=0)。
3. 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+1。
4. 具有n个结点的完全二叉树的深度k为log2(n+1)上取整。
5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的结点有:

  • 若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
  • 若2i+1<n,左孩子序号:2i+1,否则无左孩子
  • 若2i+2<n,右孩子序号:2i+2,否则无右孩子

6.当完全二叉树有偶数个节点时,n1 = 1;

7.当完全二叉树有奇数个节点时,n1 = 0;

8.n层k叉树共有节点(k^n-1)/ (k-1);

1.4二叉树基本性质定理题

1. 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( B)
A 不存在这样的二叉树
B 200
C 198
D 199


2.在具有 2n 个结点的完全二叉树中,叶子结点个数为(A )
A n
B n+1
C n-1
D n/2

3.一个具有767个节点的完全二叉树,其叶子节点个数为(B)
A 383
B 384
C 385
D 386


4.一棵完全二叉树的节点数为531个,那么这棵树的高度为(B )
A 11
B 10
C 8
D 12

1.5二叉树遍历基本操作

二叉树的存储结构分为:顺序存储和类似于链表的链式存储。

  • 代码实现:
  • NLR:前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点--->根的左子树--->根的右子树。
  • LNR:中序遍历(Inorder Traversal)——根的左子树--->根节点--->根的右子树。
  • LRN:后序遍历(Postorder Traversal)——根的左子树--->根的右子树--->根节点。
public class TestBinaryTree {

    static class TreeNode {
        public char val;
        public TreeNode left;//左孩子的引用
        public TreeNode right;//右孩子的引用

        public TreeNode(char val) {
            this.val = val;
        }
    }


    /**
     * 创建一棵二叉树 返回这棵树的根节点
     *
     * @return
     */
    public TreeNode createTree() {
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');
        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        C.left = F;
        C.right = G;
        E.right = H;
        //this.root = A;
        return A;
    }

    // 前序遍历
    public void preOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        System.out.println(root.val + " ");
        preOrder(root.left);
        preOrder(root.right);
    }

    // 中序遍历
    void inOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        inOrder(root.left);
        System.out.println(root.val + " ");
        inOrder(root.right);
    }

    // 后序遍历
    void postOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        inOrder(root.left);
        inOrder(root.right);
        System.out.println(root.val + " ");
    }

1.6二叉树遍历的前中后非递归写法

//前序遍历非递归
    public List<Integer> PreOrder(TreeNode root){
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()){
            while (cur != null){
                list.add(cur.val);
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            cur = cur.right;
        }
        return list;
    }

//中序遍历非递归
    public List<Integer> InorderTravelSal(TreeNode root){
        if (root == null){
            return new ArrayList<>();
        }
        List<Integer> list = new ArrayList<>();
        LinkedList<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            list.add(cur.val);
            cur = cur.right;
        }
        return list;
    }

//后序遍历非递归
    public List<Integer> PostOrder(TreeNode root){
        if (root == null){
            return new ArrayList<>();
        }
        List<Integer> list = new ArrayList<>();
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;
        TreeNode prev = null;
        while (cur != null || !stack.isEmpty()){
            while (cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode node = stack.peek();
            if (node.right == null || node.right == prev){
                list.add(node.val);
                stack.pop();
                prev = node;
            }else {
                cur = cur.right;
            }
        }
        return list;
    }

1.7二叉树有关的基本操作

/**
     * 获取树中节点的个数:遍历思路
     */
    void size(TreeNode root) {
        if (root == null) {
            return;
        }
        nodeSize++;
        size(root.left);
        size(root.right);
    }

/*
  获取叶子节点的个数:子问题
 */
    int getLeafNodeCount2(TreeNode root) {
        if (root == null) {
            return 0;
        }
        if (root.left == null && root.right == null) {
            return 1;
        }
        int leftSize = getLeafNodeCount2(root.left);
        int rightSize = getLeafNodeCount2(root.right);
        return leftSize + rightSize;
    }

/*
    获取第K层节点的个数
     */
    int getKLevelNodeCount(TreeNode root, int k) {
        if (root == null) {
            return 0;
        }
        if (k == 1) {
            return 1;
        }
        int leftSize = getKLevelNodeCount(root.left, k - 1);
        int rightSize = getKLevelNodeCount(root.right, k - 1);
        return leftSize + rightSize;
    }

/*
     获取二叉树的高度
     时间复杂度:O(N)
     */
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftHeight = maxDepth(root.left);
        int rightHeight = maxDepth(root.right);

        return (leftHeight > rightHeight) ?
                (leftHeight + 1) : (rightHeight + 1);
    }

// 检测值为value的元素是否存在
    TreeNode find(TreeNode root, char val) {
        if (root == null) {
            return null;
        }
        if (root.val == val) {
            return root;
        }
        TreeNode leftTree = find(root.left, val);
        if (leftTree != null) {
            return leftTree;
        }
        TreeNode rightTree = find(root.right, val);
        if (rightTree != null) {
            return rightTree;
        }
        return null;//没有找到
    }

二、二叉树常见的OJ题

2.1相同的树

100. 相同的树 - 力扣(LeetCode)

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if(p == null && q == null){
            return true;
        }
        if(p != null && q == null || p == null && q != null){
            return false;
        }
        if(p.val != q.val){
            return false;
        }
        return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);

    }
}

 2.2对称二叉树

 101. 对称二叉树 - 力扣(LeetCode)

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null){return true;}
        return isSymmetricChild(root.left,root.right);
        }
        
        public boolean isSymmetricChild(TreeNode leftTree,TreeNode rightTree){
        if (leftTree != null && rightTree == null ||
                leftTree == null && rightTree != null){
            return false;
        }
        if (leftTree == null && rightTree == null){
            return true;
        }
        if (leftTree.val != rightTree.val){
            return false;
        }
        return isSymmetricChild(leftTree.left,rightTree.right) &&
                isSymmetricChild(leftTree.right,rightTree.left);
        }
}

与上一道相同的树原理一致。

2.3反转二叉树

226. 翻转二叉树 - 力扣(LeetCode)

 

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null){
            return null;
        }
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;

        invertTree(root.left);
        invertTree(root.right);

        return root;
    }
}

注意将子节点的每个节点都要反转

2.4二叉树的公共祖先

最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)

236. 二叉树的最近公共祖先 - 力扣(LeetCode)

class Solution {
    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);
        TreeNode rightRet = lowestCommonAncestor(root.right,p,q);
        if(leftRet != null && rightRet != null){
            return root;
        }else if(leftRet != null){
            return leftRet;
        }else if(rightRet != null){
            return rightRet;
        }
        return null;
    }
}

注意在左右子树中寻找节点,如果找到并返回节点,否则返回空

2.5二叉树的层序遍历

102. 二叉树的层序遍历 - 力扣(LeetCode)

class Solution {
        public List<List<Integer>> levelOrder(TreeNode root) {
            List<List<Integer>> list = new ArrayList<>();
            if (root == null) {
                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();
                    tmp.add((int) cur.val);
                    if (cur.left != null) {
                        queue.offer(cur.left);
                    }
                    if (cur.right != null) {
                        queue.offer(cur.right);
                    }
                    size--;
                }
                list.add(tmp);
            }
            return list;
        }
    }

本题需要注意层序遍历要使用队列,先进先出。

2.6根据二叉树前序与中序序列构造二叉树

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

105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return buildTreechild(preorder,inorder,0,inorder.length-1);
    }
    int i = 0;
    public TreeNode buildTreechild(int[] preorder,int[] inorder,int inbegin,int inend){
        if(inbegin > inend){
            return null;
        }
        TreeNode root = new TreeNode(preorder[i]);
        int rootIndex = findIndex(inorder,inbegin,inend,preorder[i]);
        i++;
        root.left = buildTreechild(preorder,inorder,inbegin,rootIndex-1); 
        root.right = buildTreechild(preorder,inorder,rootIndex+1,inend);
        return root; 
    }
    public int findIndex(int[] inorder,int inbegin,int inend,int key){
        for(int i = inbegin;i <= inend;i++){
            if(inorder[i]==key){
                return i;
            }
        }
        return -1;
    }
}

本题需要注意的点是只有给出前序或后续序列才能确定中序中根的位置

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

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

相关文章

聚观早报 | 货拉拉入局跑腿业务;苹果任命首位首席人力资源官

今日要闻&#xff1a;谷歌AI聊天机器人Bard股价大跌7.4%&#xff1b;货拉拉入局跑腿业务&#xff1b;苹果任命首位首席人力资源官&#xff1b;迪士尼宣布裁员 7000 人&#xff1b;家乐福中国 COO 离职 谷歌AI聊天机器人Bard股价大跌 7.4% 2 月 8 日消息&#xff0c;谷歌人工智能…

一篇五分生信临床模型预测文章代码复现——FIgure 9.列线图构建,ROC分析,DCA分析 (四)

之前讲过临床模型预测的专栏,但那只是基础版本,下面我们以自噬相关基因为例子,模仿一篇五分文章,将图和代码复现出来,学会本专栏课程,可以具备发一篇五分左右文章的水平: 本专栏目录如下: Figure 1:差异表达基因及预后基因筛选(图片仅供参考) Figure 2. 生存分析,…

软件使用【SecureCRT】 SSH连接报错Key exchange failed

目录 一、原因分析 二、解决方法 三、修改文件方法 1、修改ssh_config 2、修改sshd_config 3、重新启动服务 SecureCRT连接服务器时报错&#xff0c;报错信息为&#xff1a; Key exchange failed. No compatible key exchange method. The server supports these methods…

轨迹预测算法vectorNet调研报告

前言 传统的行为预测方法是规则的&#xff0c;基于道路结构的约束生成多个行为假设。最近&#xff0c;很多基于学习的预测方法被提出。他们提出了对于不同行为假设的进行概率解释的好处&#xff0c;但是需要重构一个新的表示来编码地图和轨迹信息。有趣的是&#xff0c;虽然高精…

【论文阅读】TDANet:一种具有自上而下注意力的用于语音分离的高效自编码器架构(ICLR 2023)

TDANet: 一种具有自上而下注意力的用于语音分离的高效自编码器架构 文章目录TDANet: 一种具有自上而下注意力的用于语音分离的高效自编码器架构速览摘要方法PipelineTDANet实验总结速览 下载收录源码机构演示arxivICLR 2023PyTorch清华大学Demo inproceedings{tdanet2023iclr,…

Redis应用场景

redis的五种基本数据类型结构类型结构存储的值结构的读写能力String字符串可以是字符串、整数或浮点数对整个字符串或字符串的一部分进行操作&#xff1b;对整数或浮点数进行自增或自减操作&#xff1b;List列表一个链表&#xff0c;链表上的每个节点都包含一个字符串对链表的两…

Docker进阶 - 9. docker network 之自定义网络

1. 运行两个tomcat实例&#xff0c;并进入容器内部 docker run -d -p 8081:8080 --name tomcat81 billygoo/tomcat8-jdk8 docker exec -it tomcat81 bashdocker run -d -p 8082:8080 --name tomcat82 billygoo/tomcat8-idk8 docker exec -it tomcat82 bash2. ping一下各自的ip…

Windeployqt 打包,缺少dll 的解决方法

Windeployqt 打包&#xff0c;缺少DLL 的原因分析&#xff0c;解决方法 很多同学使用工具windeployqt进行打包发布后&#xff0c;运行exe文件时&#xff0c;还是会出现下图所示的系统错误提示&#xff0c;这种情况就表示相关的DLL 库文件没有被正确打包。可是windeployqt明确显…

20230210使AIO-3568J开发板在Android12下调通3个USB

20230210使AIO-3568J开发板在Android12下调通3个USB 2023/2/10 10:00 0、默认编译RK3568的Andorid12的rk3568-evb2-lp4x-v10.dts&#xff0c;2个USB2.0接口的鼠标可以用。 并列USB3.0接口的上面的鼠标不能用。USB3.0接口下面可以连接ADB。 vcc5v0_host: vcc5v0-host-regula…

邀您参赛!DCIC 2023「科技金融欺诈风险识别」算法赛正在报名中

近年来&#xff0c;跨境赌博、电信网络诈骗、黑产等外部欺诈违法犯罪形势日益严峻&#xff0c;呈现线上化、产业化、团伙化等特征&#xff0c;国家、监管机构及银行自身都高度重视反欺诈治理工作&#xff0c;坚决守护人民群众的财产安全。 为进一步打击外部欺诈违法犯罪行为&am…

CentOS7 ifconfig(或 ip addr)命令不显示IP地址

问题&#xff08;因为当时没有存图 所以这个图上是网上找的 &#xff09;解决办法第一&#xff1a;可能是本地服务没有开启&#xff0c;检查本地服务。如图所示&#xff0c;检查这两个服务是否开启。注&#xff1a;如何快速找到服务 可以把光标放在其中一个上面 然后按下VM就可…

硬盘分类及挂载硬盘知识补充和介绍

一、硬盘介绍Linux硬盘分IDE硬盘和SCSI硬盘&#xff0c;目前基本上是SCSI硬盘1.对于IDE硬盘&#xff0c;驱动器标识符为"hdx~"&#xff0c;其中"hd"表明分区所在设备的类型&#xff0c;这里是指IDE硬盘了。"x"为盘号(a为基本盘&#xff0c;b为基…

endo-BCN-PEG4-Palmitic,环丙烷环辛炔四聚乙二醇-Palmitic包装灵活

endo-BCN-PEG4-Palmitic&#xff0c;endo环丙烷环辛炔四聚乙二醇-Palmitic反应特点&#xff1a;endo-BCN-PEG4-Palmitic 酯在其末端含有一个 Palmitic基和一个 BCN 基。对点击试剂来说同样会通过不同的小分子PEG进行连接&#xff0c;BCN-PEG-acid、BCN-PEG-NHS ester、BCN-PEG-…

软件测试—对职业生涯发展的一些感想

目录&#xff1a;导读 职场生涯 1、短期规划 2、长期规划 自身定位 1、你在哪儿&#xff1f; 2、你想要什么&#xff1f; 3、你拥有什么&#xff1f; 4、你需要做什么&#xff1f;什么时候做&#xff1f; 5、淡定啊淡定 最近工作不是很忙&#xff0c;有空都是在看书&a…

多传感器融合定位十一-基于滤波的融合方法Ⅱ

多传感器融合定位十一-基于滤波的融合方法Ⅱ1. 编码器运动模型及标定1.1 编码器基础知识1.2 编码器运动模型1.2.1 旋转半径求解1.2.2 角速度求解1.2.3 线速度求解1.2.4 位姿求解1.3 编码器的标定1.3.1 轮子半径标定1.3.2 轮子与底盘中心距离标定2. 融合编码器的滤波方法2.1 核心…

调用chatgpt的api, 必须知道的三件事

牙叔教程 简单易懂 调用api的代码 let url "https://api.openai.com/v1/completions"; let answer await axios // 使用axios发送post请求.post(url, data, { headers: headers }).then((res) > {return res.data.choices[0].text.trim();}).catch((err) >…

谈谈会话管理

客户端和服务器之间进行数据传输遵循的是HTTP协议, 此协议属于无状态协议(一次请求对应一次响应, 响应完之后断开连接), 服务器是无法跟踪客户端的请求, 通过cookie技术可以给客户端添加一个标识, 客户端之后发出的每次请求都会带着这个标识从而让服务器识别此客户端, 但由于co…

PostgreSQL入门

PostgreSQL入门 简介 PostgreSQL是以加州大学伯克利分校计算机系开发的POSTGRES&#xff0c; 版本 4.2为基础的对象关系型数据库管理系统&#xff08;ORDBMS&#xff09; 支持大部分SQL标准并且提供了许多现代特性 复杂查询外键触发器可更新视图事务完整性多版本并发控制 …

引导滤波code

文章目录1. 原理概述2. 实验环节2.1 验证与opencv 库函数的结果一致2.2 与 双边滤波比较2.3 引导滤波应用&#xff0c;fathering2.3 引导滤波应用&#xff0c;图像增强2.4 灰度图引导&#xff0c;和各自通道引导的效果差异2.5 不同参数设置影响3. 参考引导滤波1. 原理概述 引导…

VHDL语言基础-状态机设计-ASM图法状态机设计

目录 有限状态机的描述方法&#xff1a; ASM图&#xff1a; 状态转移图&#xff1a; 状态转移列表&#xff1a; MDS图&#xff1a; ASM图法状态机设计&#xff1a; ASM图的组成&#xff1a; 状态框&#xff1a; 判断框&#xff1a; 条件框&#xff1a; 状态框与条件框…