Java|二叉树基础详解
文章目录
- 树结构的引入
- 关于树的基础概念
- 树的特点
- 树的概念
- 二叉树
- 二叉树常考的性质
- 常见二叉树
- 关于完全二叉树的编号
- 二分搜索树(BST)
- 平衡二叉树
- 二叉树的实现
- 二叉树的基本操作
- 遍历
- 前序遍历
- 中序遍历
- 后序遍历
- 层序遍历
- 前中后序遍历的结果
- 二叉树基本题
- 求结点个数
- 求叶子结点个数
- 求第k层结点个数
- 求二叉树的高度
- 判断二叉树中是否包含值val
- 总结
树结构的引入
树:高效的查找与搜索语义
在OS中的文件系统就是基于树结构进行文件管理的
如果所有文件都放在一个“目录”下,若当前操作系统有1亿个文件,最坏情况遍历1亿次才能找到特定元素
OS分为多级目录,我们只需要O(logn)的时间复杂度就可以找到指定元素
关于树的基础概念
我们已经知道,线性数据结构或者说线性表的元素之间是逻辑上连续的,呈直线排列
而树是一种非线性的数据结构,它是有n个有限结点组成一个具有层次关系的集合
树的特点
树有一个特殊的结点,称为根结点,根结点没有前驱结点
除根结点,其余结点被分成m个集合,彼此之间互不相交,若相交,就不是树
每棵子树有且只有一个前驱,但可以有0或者多个后继
树的概念
1.子树不相交
2.除了根结点没有父结点之外,每个结点有且只有一个父结点
3 树,边的个数x以及结点的个数n之间的关系:x = n - 1 (每个结点都有一个与父结点相连的边,只有根结点没有)
4.结点和树的度:
结点的度:该结点中包含的子树个数称为该结点的度
树的度:书中最大的结点的度称为了树的度
5.叶子结点:度为0的结点
非叶子结点:度不为0的结点,说明还存在子树
6.根结点:没有父结点的结点,树中有且只有一个根结点
7.结点的层次:从根结点开始计算,根结点为第一层
树的高度:当前树中结点层次最大的即为树的高度
二叉树
树中结点最大的度为2的树称之为二叉树
二叉树中,一个结点最多有两颗子树,二叉树结点的度小于等于2
且子树有左右之分,左右的顺序不能颠倒,分为只有左子树没有右子树的结点,只有右子树没有左子树的结点
二叉树常考的性质
1.在深度为k的二叉树中,最多存在2^k - 1个结点
2.在第k层,该层最多有2^(k -1)个结点
3.由于任意二叉树都满足结点个数n和边长具备x = n - 1
在二叉树中,度为2的结点和度为0的结点有以下关系:n0 = n2 + 1
度为0度结点个数永远比度为2的结点的个数多一个
关于第三种性质的推导:
二叉树中只有三种结点:n0, n1, n2
n0 + n1 + n2 = n ( 三种结点的个数相加就是总结点的个数 )
n1 + 2n2 = n - 1 ( n1有一条边,n2有两条边,相加后总共有 n - 1条边 )
将两个式子化简可以得到:n0 = n2 + 1
常见二叉树
满二叉树:在该二叉树中,每一层的结点个数都是最大值
完全二叉树:完全二叉树的结点编号和满二叉树完全一致,一一对应
满二叉树就是一个特殊的完全二叉树
在完全二叉树中不存在只有右子树而没有左子树的结点,换言之,如果存在度为1的结点,这个结点必然只有左树而没有右树的结点,这样的结点有且只有一个
关于完全二叉树的编号
1.若根结点从1开始编号,若任意一个结点x,即存在子树也有父结点,则该结点的父结点编号为x / 2,其左子树编号为2x,右子树编号为2x + 1
2.若根结点从0开始编号(“堆”就是基于数组实现的完全二叉树):则在完全二叉树中,若任意结点既有子树也有父结点,该结点编号为x,则父结点的编号为(x - 1) / 2,左子树的结点编号为2x + 1,右子树的结点编号为2x + 2
二分搜索树(BST)
结点的值之间有一个大小关系:左子树结点值 < 根结点值 < 右子树结点值
若在BST中查找一个元素,其实就是二分查找
平衡二叉树
该树中任意一个结点的左右子树高度差 <= 1
注意是所有结点都要满足,仅仅是根结点的子树满足情况是不够的,因为子树的子树可能不平衡
二叉树的实现
树结点:
class TreeNode<E> {E val;TreeNode<E> left;TreeNode<E> right;public TreeNode(E val){this.val = val;}
}
二叉树的纯手工构建:
public void build(){TreeNode<Character> node = new TreeNode<>('A');TreeNode<Character> node1 = new TreeNode<>('B');TreeNode<Character> node2 = new TreeNode<>('C');TreeNode<Character> node3 = new TreeNode<>('D');TreeNode<Character> node4 = new TreeNode<>('E');TreeNode<Character> node5 = new TreeNode<>('F');TreeNode<Character> node6 = new TreeNode<>('G');TreeNode<Character> node7 = new TreeNode<>('H');node.left = node1;node.right = node2;node1.left = node3;node1.right = node4;node4.left = node6;node6.right = node7;node2.right = node5;root = node;
}
构建完成后的二叉树见后图
二叉树的基本操作
所有二叉树的基础操作,或者说所有二叉树的问题的解决思路都是遍历问题的衍生
遍历
遍历是指按照一定的顺序访问这个集合的所有元素,做到不重复不遗漏
对于二叉树来说一共有四种遍历方式:前序遍历、中序遍历、后序遍历、层序遍历
前三种称为深度优先遍历(DFS),最后一种称为广度优先遍历(BFS)
深度优先遍历借助栈实现,广度优先遍历借助队列实现
二叉树图例:
前序遍历
先访问根结点,然后递归访问左子树,再递归问右子树(根左右)
上图为例的遍历结果:A B D E G H C F
代码实现:
/*** 传入根结点,就能按照前序遍历的方式输出 - 根左右* @param root*/
public void preOrder(TreeNode root){if(root == null){return;}System.out.print(root.val + " ");preOrder(root.left);preOrder(root.right);
}
中序遍历
先递归的访问左子树,然后访问根结点,最后访问右子树(左根右)
当根结点第二次走到时才能访问
上图为例的遍历结果:D B G H E A C F
代码实现:
/*** 传入根结点,就能按照中序遍历的方式输出 - 左根右* @param root*/
public void inOrder(TreeNode root){if(root == null){return;}inOrder(root.left);System.out.print(root.val + " ");inOrder(root.right);
}
后序遍历
先递归访问左子树,在递归访问右子树,最后访问根结点
上图为例的遍历结果:D H G E B F C A
代码实现:
/*** 传入根结点,就能根据后序遍历的方式输出 - 左右根* @param root*/
public void postOrder(TreeNode root){if(root == null){return;}postOrder(root.left);postOrder(root.right);System.out.print(root.val + " ");
}
层序遍历
将二叉树一层一层遍历,从上往下,从左至右
前三种遍历通过画栈的方式来辅助遍历顺序
上图为例的遍历结果:A B C D E F G H
层序遍历需要借助递归函数和队列来实现,因此在之后的博客中会专门讲解前中后序以及层序遍历
前中后序遍历的结果
- 前序遍历的第一个结点一定是根结点
- 中序遍历左子树在根结点的左侧,右子树的遍历结果在根结点的右侧
- 后序遍历的结果集反转之后是(根右左),后序遍历的最后一个结果一定为根结点
二叉树基本题
求结点个数
public int getNodes(TreeNode root){if(root == null){return 0;}//如果根结点不为空,那么返回自身加上左子树的结点个数和右子树的结点个数之和return 1 + getNodes(root.left) + getNodes(root.right);//可以简化为一句话//return root == null ? 0 : 1 + getNodes(root.left) + getNodes(root.right);
}
求叶子结点个数
public int getLeafNode(TreeNode root){if(root == null){return 0;}if(root.left == null && root.right == null){return 1;}return getLeafNode(root.left) + getLeafNode(root.right);
}
求第k层结点个数
public int getKLevelNodes(TreeNode root, int k){if(k < 1 || root == null){return 0;}if(k == 1){//当k为1时,说明只有根结点这一层,因此直接返回1即可return 1;}//此时k不为1,就继续找下一层return getKLevelNodes(root.left, k - 1) + getKLevelNodes(root.right, k - 1);
}
求二叉树的高度
public int height(TreeNode root){if(root == null){return 0;}//树的高度 = 当前层 + 子树中的高度return 1 + Math.max(height(root.left), height(root.right));
}
判断二叉树中是否包含值val
public boolean contains(TreeNode<E> root, E val){if(root == null){return false;}if(root.val == val){return true;}return contains(root.left, val) || contains(root.right, val);
}
总结
该博客仅针对二叉树的基础部分做一个介绍,关于二叉树的内容、题目还有很多,之后会陆续发布关于前中后序以及层序遍历的详细解法,以及一些常考笔试、面试题的解答,如果有需要的话,就点个小小的关注吧~~
Java|二叉树基础详解相关推荐
- Java :内部类基础详解
可以将一个类的定义放在另一个类的定义内部,这就是内部类. 第一次见面 内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类. public class OuterClass {pri ...
- Java 接口基础详解,java开发面试笔试题
我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家. 扫描二维码或搜索下图红色VX号,加VX好友,拉你进[程序员面试学习交流群]免费领取.也欢迎各位一起 ...
- Java抽象类(基础详解)
目录 1.概念 2.抽象类的使用 2.1抽象方法 2.2抽象类 2.3抽象类的使用 2.4注意事项 1.概念 普通类是一个完善的功能类,可以直接产生实例化对象,并且在普通类中可以包含有构造方法,普通方 ...
- java常用集合详解
文章目录 一.常用集合大纲 1.常用集合框架及介绍 2.集合和数组的区别 二.Collection 集合(接口) 三.List集合(接口) 1.存储遍历方式 2.ArrayList(实现类) 3.Li ...
- 4.6 W 字总结!Java 11—Java 17特性详解
作者 | 民工哥技术之路 来源 | https://mp.weixin.qq.com/s/SVleHYFQeePNT7q67UoL4Q Java 11 特性详解 基于嵌套的访问控制 与 Java 语言 ...
- Java串口通信详解(转)
Java串口通信详解(转) 作者:denimcc 日期:2007-05-11 序言 说到开源,恐怕很少有人不挑大指称赞.学生通过开源代码学到了知识,程序员通过开源类库获得了别人的成功经验及能够 ...
- Python学习二:词典基础详解
作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/7862377.html 邮箱:moyi@moyib ...
- 线程状态,优先级,守护线程基础详解
线程状态,优先级,守护线程基础详解 线程状态 停止线程 线程休眠 线程礼让 线程强制执行 线程状态检测 线程的优先级 守护线程 线程同步 线程状态 创建状态(new 之后就是创建状态 就绪状态(调用s ...
- java多线程设计模式详解[推荐]
java多线程设计模式详解[推荐] java多线程设计模式详解之一 线程的创建和启动 java语言已经内置了多线程支持,所有实现Runnable接口的类都可被启动一个新线程,新线程会执行该实例的run ...
- php java集成_PHP和Java 集成开发详解分析 强强联合第1/4页
PHP和Java 集成开发详解分析 强强联合第1/4页 更新时间:2008年11月14日 12:28:23 作者: 很久以前,有人从www上看到看到天空上一个很亮的亮点,它就是Java语言,与此同 ...
最新文章
- js中字符串转为对象或者json
- HDOJ 1082 模拟 水
- 作为硬通货的学术引用,何以统治学术圈?
- JimStoneAjax如何跟DWR竞争?
- ASP.NET AJAX入门系列(8):自定义异常处理
- 怎样和处在“叛逆”阶段的孩子交流沟通?
- 【转】LDA-linear discriminant analysis
- linux Rh界面,Ubuntu Linux与RH系列的不同之处
- VScode中快速生成vue模板
- 阿里云-对象存储OSS
- 9门主流编程语言---详细对比
- contrastive CAM
- 本科挣 30 万,秋招大厂提前批offer,大学四年,帅地做对了什么?(附所有知识清单)
- 共享个人整理的Python问题,有源码,分析过程,解决方案,还有时间戳做间隔
- windows ios良心软件推荐
- l7sa008b故障代码_华硕主板故障维代码指南
- 2021年12月中国A股上市企业股价涨幅排行榜:三羊马涨幅最大,从事传媒行业的企业最多(附月榜TOP100详单)
- 学习Linux系统编程、网络编程的方法
- 前端开发入门到实战:六种组织CSS的方式
- 笔记本有没有必要加内存条?
热门文章
- LM2596电源降压调整器(150KHZ 3A)原理图中文版
- 界面控件DotNetBar for WinForms使用教程:LayoutControl布局与通用代码设置(三)
- 雪球产品定价-蒙特卡罗模拟法
- 十三、Linux驱动之触摸屏驱动
- wifi mesh测试软件,「可能是」最适合我的Mesh+WiFi6方案:Linksys Velop MX10600体验分享...
- Python语音识别终极指南(收藏)
- 马哥Linux笔记整理
- 特殊矩阵——三对角矩阵(Tridiagonal Matrix)
- 自来水公司SCADA调度系统方案
- android tabhost用法详解,android Tabhost部件详解