文章目录

  • 树结构的引入
  • 关于树的基础概念
    • 树的特点
    • 树的概念
  • 二叉树
    • 二叉树常考的性质
    • 常见二叉树
    • 关于完全二叉树的编号
    • 二分搜索树(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

层序遍历需要借助递归函数和队列来实现,因此在之后的博客中会专门讲解前中后序以及层序遍历

前中后序遍历的结果

  1. 前序遍历的第一个结点一定是根结点
  2. 中序遍历左子树在根结点的左侧,右子树的遍历结果在根结点的右侧
  3. 后序遍历的结果集反转之后是(根右左),后序遍历的最后一个结果一定为根结点

二叉树基本题

求结点个数

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|二叉树基础详解相关推荐

  1. Java :内部类基础详解

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 第一次见面 内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类. public class OuterClass {pri ...

  2. Java 接口基础详解,java开发面试笔试题

    我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家. 扫描二维码或搜索下图红色VX号,加VX好友,拉你进[程序员面试学习交流群]免费领取.也欢迎各位一起 ...

  3. Java抽象类(基础详解)

    目录 1.概念 2.抽象类的使用 2.1抽象方法 2.2抽象类 2.3抽象类的使用 2.4注意事项 1.概念 普通类是一个完善的功能类,可以直接产生实例化对象,并且在普通类中可以包含有构造方法,普通方 ...

  4. java常用集合详解

    文章目录 一.常用集合大纲 1.常用集合框架及介绍 2.集合和数组的区别 二.Collection 集合(接口) 三.List集合(接口) 1.存储遍历方式 2.ArrayList(实现类) 3.Li ...

  5. 4.6 W 字总结!Java 11—Java 17特性详解

    作者 | 民工哥技术之路 来源 | https://mp.weixin.qq.com/s/SVleHYFQeePNT7q67UoL4Q Java 11 特性详解 基于嵌套的访问控制 与 Java 语言 ...

  6. Java串口通信详解(转)

    Java串口通信详解(转) 作者:denimcc 日期:2007-05-11 序言     说到开源,恐怕很少有人不挑大指称赞.学生通过开源代码学到了知识,程序员通过开源类库获得了别人的成功经验及能够 ...

  7. Python学习二:词典基础详解

    作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/7862377.html 邮箱:moyi@moyib ...

  8. 线程状态,优先级,守护线程基础详解

    线程状态,优先级,守护线程基础详解 线程状态 停止线程 线程休眠 线程礼让 线程强制执行 线程状态检测 线程的优先级 守护线程 线程同步 线程状态 创建状态(new 之后就是创建状态 就绪状态(调用s ...

  9. java多线程设计模式详解[推荐]

    java多线程设计模式详解[推荐] java多线程设计模式详解之一 线程的创建和启动 java语言已经内置了多线程支持,所有实现Runnable接口的类都可被启动一个新线程,新线程会执行该实例的run ...

  10. php java集成_PHP和Java 集成开发详解分析 强强联合第1/4页

    PHP和Java 集成开发详解分析 强强联合第1/4页 更新时间:2008年11月14日 12:28:23   作者: 很久以前,有人从www上看到看到天空上一个很亮的亮点,它就是Java语言,与此同 ...

最新文章

  1. js中字符串转为对象或者json
  2. HDOJ 1082 模拟 水
  3. 作为硬通货的学术引用,何以统治学术圈?
  4. JimStoneAjax如何跟DWR竞争?
  5. ASP.NET AJAX入门系列(8):自定义异常处理
  6. 怎样和处在“叛逆”阶段的孩子交流沟通?
  7. 【转】LDA-linear discriminant analysis
  8. linux Rh界面,Ubuntu Linux与RH系列的不同之处
  9. VScode中快速生成vue模板
  10. 阿里云-对象存储OSS
  11. 9门主流编程语言---详细对比
  12. contrastive CAM
  13. 本科挣 30 万,秋招大厂提前批offer,大学四年,帅地做对了什么?(附所有知识清单)
  14. 共享个人整理的Python问题,有源码,分析过程,解决方案,还有时间戳做间隔
  15. windows ios良心软件推荐
  16. l7sa008b故障代码_华硕主板故障维代码指南
  17. 2021年12月中国A股上市企业股价涨幅排行榜:三羊马涨幅最大,从事传媒行业的企业最多(附月榜TOP100详单)
  18. 学习Linux系统编程、网络编程的方法
  19. 前端开发入门到实战:六种组织CSS的方式
  20. 笔记本有没有必要加内存条?

热门文章

  1. LM2596电源降压调整器(150KHZ 3A)原理图中文版
  2. 界面控件DotNetBar for WinForms使用教程:LayoutControl布局与通用代码设置(三)
  3. 雪球产品定价-蒙特卡罗模拟法
  4. 十三、Linux驱动之触摸屏驱动
  5. wifi mesh测试软件,「可能是」最适合我的Mesh+WiFi6方案:Linksys Velop MX10600体验分享...
  6. Python语音识别终极指南(收藏)
  7. 马哥Linux笔记整理
  8. 特殊矩阵——三对角矩阵(Tridiagonal Matrix)
  9. 自来水公司SCADA调度系统方案
  10. android tabhost用法详解,android Tabhost部件详解