作者:Optimism&Active

www.cnblogs.com/xisuo/p/11921647.html

目录

前言
1.数组和链表的特点
2.树和二叉树
3.二叉搜索树以及它是通过什么方式改善的数组、链表的问题
4.二叉树的实现

  • 4.1 新增数据

  • 4.3 查找方法

  • 4.3 删除方法

5.遍历
6.二叉树的效率
7.二叉树的缺点

前言

本篇博客为将为大家介绍一下数据结构---二叉树,它在保留数组和链表的优点的同时也改善了它们的缺点(当然它也有着自己的缺点,同时它的实现也比较复杂).

1. 数组和链表的特点

数组的优点:

  • 简单易用.

  • 无序数组的插入速度很快,效率为O(1)

  • 有序数组的查找速度较快(较无序数组),效率为O(logN)

数组的缺点:

  • 数组的查找、删除很慢

  • 数组一旦确定长度,无法改变

链表的优点:

  • 可以无限扩容(只要内存够大)

  • 在链表头的新增、删除很快,效率为O(1)

链表的缺点:

  • 查找很慢

  • 在非链表头的位置新增、删除很慢,效率为O(N)

2.树和二叉树

树是一种数据结构,因为它数据的保存形式很像一个树,所以得名为树(树状图).

而二叉树是一种特殊的树,它的每个节点最多含有两个子树,现实世界中的二叉树:

图1

但是实际中的二叉树却是倒挂的,如图:


图2

二叉树的名词解释:

  • 根:树顶端的节点称为根。一棵树只有一个根,如果要把一个节点和边的集合称为树,那么从根到其他任何一个节点都必须有且只有一条路径。A是根节点。

  • 父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;B是D的父节点。

  • 子节点:一个节点含有的子树的根节点称为该节点的子节点;D是B的子节点。

  • 兄弟节点:具有相同父节点的节点互称为兄弟节点;比如上图的D和E就互称为兄弟节点。

  • 叶节点:没有子节点的节点称为叶节点,也叫叶子节点,比如上图的E、H、L、J、G都是叶子节点。

  • 子树:每个节点都可以作为子树的根,它和它所有的子节点、子节点的子节点等都包含在子树中。

  • 节点的层次:从根开始定义,根为第一层,根的子节点为第二层,以此类推。

  • 深度:对于任意节点n,n的深度为从根到n的唯一路径长,根的深度为0;

  • 高度:对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0;

深度与高度的区别在于:深度为根到节点的距离,而高度是节点到叶的距离(记住根深叶高)。

3.二叉搜索树以及它是通过什么方式改善的数组、链表的问题

二叉搜索树是一种特殊的二叉树,除了它的子节点不能超过两个以外,它还拥有如下特点:

  • 一个节点的左子节点的关键字的值永远小于该节点的值

  • 一个节点的右子节点的关键字的值永远大于等于该节点的值


图3 - 二叉搜索树关键字的排序方式

从图3还可以看出,二叉搜索树的最小值就是它的最左节点的关键字的值,而最大值则是它的最右节点的值.

二叉搜索树的查找、新增、删除的效率为O(logN)(这是理想状态下,如果树是不平衡的效率会降到O(N),后面会介绍).

二叉搜索树之所以效率高就在于:

  • 它的数据是按照上述的有序的方式排列的.

  • 进行新增、查找、删除的时候使用了二分查找法.

4. 二叉树的实现

二叉树中数据是保存在一个个的节点中的,下面是保存数据的节点类:

/*** @author liuboren* @Title: 节点类* @Description:*/
public class Node {// 用来进行排序的关键字数组int sortData ;// 其他类型的数据int other;// 该节点的左子节点Node leftNode;// 该节点的右子节点Node rightNode;public static void main(String[] args) {Node node = new Node();System.out.println("node.leftNode = " + node.leftNode);System.out.println(node.leftNode);}}

在二叉搜索树这个类中新增、修改、删除数据:

public class Tree {// 根节点Node root;public Tree(Node root) {this.root = root;}// 新增、查找、删除 暂时省略,下面会一一介绍
}

4.1 新增数据

在二叉树中插入数据的流程如下:


图4


图5

Java代码:

 /*新增数据*/public void insertData(Node node) {int currentSortData = root.sortData;Node currentNode = root;Node currentLeftNode = root.leftNode;Node currentRightNode = root.rightNode;int insertSortData = node.sortData;while (true) {if (insertSortData < currentSortData) {if (currentLeftNode == null) {currentNode.leftNode = node;break;} else {currentNode = currentNode.leftNode;currentLeftNode = currentNode.leftNode;currentRightNode = currentNode.rightNode;currentSortData = currentNode.sortData;}} else {if (currentRightNode == null) {currentNode.rightNode = node;break;} else {currentNode = currentNode.rightNode;currentSortData = currentNode.sortData;currentLeftNode = currentNode.leftNode;currentRightNode = currentNode.rightNode;}}}System.out.println("root = " + root);}

4.3 查找方法

流程与插入方法类似.(Java知音公众号内回复“面试题聚合“,送你一份面试题宝典)

Java代码:

public void query(int sortData) {Node currentNode = root;while (true) {if (sortData != currentNode.sortData) {if (sortData < currentNode.sortData) {if (currentNode.leftNode != null) {currentNode = currentNode.leftNode;} else {System.out.println("对不起没有查询到数据");}} else {if (currentNode.rightNode != null) {currentNode = currentNode.rightNode;} else {System.out.println("对不起没有查询到数据");}}} else {System.out.println("二叉树中有该数据");}}
}

4.3 删除方法

删除节点要分三种情况.

  • 删除节点无子节点的情况

  • 删除节点有一个子节点的情况

  • 删除节点有两个子节点的情况

删除节点无子节点的情况是最简单的,直接将该节点置为null就可以了:


图6

删除节点有一个子节点的情况:


图7

删除后:

图8

最复杂的删除节点有两个子节点的情况,删除流程如下:


图9

删除后:


图10

为什么要以这种方式删除节点呢? 再次回顾一下二叉搜索树的特点:

  • 一个节点的左子节点的关键字的值永远小于该节点的值

  • 一个节点的右子节点的关键字的值永远大于等于该节点的值

之所以要找删除节点的右子节点的最后一个左节点,是因为这个值是删除节点的子节点中最小的值,为了满足上面的这两个特点,所以删除要以这种算法去实现.更多排序算法扩展:排序算法内容聚合

Java代码:

 public boolean delete(int deleteData) {Node curr = root;Node parent = root;boolean isLeft = true;while (deleteData != curr.sortData) {if (deleteData <= curr.sortData) {isLeft = true;if (curr.leftNode != null) {parent = curr;curr = curr.leftNode;}} else {isLeft = false;if (curr.rightNode != null) {parent = curr;curr = curr.rightNode;}}if (curr == null) {return false;}}// 删除节点没有子节点的情况if (curr.leftNode == null && curr.rightNode == null) {if (curr == root) {root = null;} else if (isLeft) {parent.leftNode = null;} else {parent.rightNode = null;}//删除节点只有左节点} else if (curr.rightNode == null) {if (curr == root) {root = root.leftNode;} else if (isLeft) {parent.leftNode = curr.leftNode;} else {parent.rightNode = curr.leftNode;}//如果被删除节点只有右节点} else if (curr.leftNode == null) {if (curr == root) {root = root.rightNode;} else if (isLeft) {parent.leftNode = curr.rightNode;} else {parent.rightNode = curr.rightNode;}} else {Node successor = getSuccessor(curr);if (curr == root) {root = successor;} else if (curr == parent.leftNode) {parent.leftNode = successor;} else {parent.rightNode = successor;}successor.leftNode = curr.leftNode;}return true;}public Node getSuccessor(Node delNode) {Node curr = delNode.rightNode;Node successor = curr;Node sucParent = null;while (curr != null) {sucParent = successor;successor = curr;curr = curr.leftNode;}if (successor != delNode.rightNode) {sucParent.leftNode = successor.rightNode;successor.rightNode = delNode.rightNode;}return successor;}

5. 遍历

遍历二叉树中的数据,有三种遍历方式:

  • 前序

  • 中序(最常用)

  • 后续

前序、中序和后序三种遍历方式的步骤是相同的,只是顺序不同.

前序遍历顺序:

  • 先输出当前节点

  • 再遍历左子节点

  • 再遍历右子节点

中序遍历顺序:

  • 先遍历左子节点

  • 再输出当前节点

  • 再遍历右子节点

后序遍历顺序:

  • 先遍历左子节点

  • 再遍历又子节点

  • 再输出当前节点

什么当前节点?什么左右子节点?太抽象!!!!没关系继续看图.

前序遍历输出顺序图:


图11

中序遍历输出顺序图:


图12

后序遍历输出顺序图:


图13

可以看出所谓的前中后序是输出当前节点的顺序,前序是在第一个输出当前节点,中序是第二个输出当前节点,后序是第三个当前节点.

又因为中序遍历是按照关键值由小到大的顺序输出的,所以中序遍历最为常用.

前、后序遍历在解析或分析二叉树(不是二叉搜索树)的算术表达式的时候比较有用,用的不太多,看下图:

6. 二叉树的效率

我们用二叉树与数组和链表进行对比,在有100w个数据项的无序数组或链表中,查找数据项平均会比较50w次,但在有100w个节点的树中,只需要20(或更少)次的比较.

有序数组可以很快的找到数据项,但插入数据项平均需要移动50w个数据项,在100w个节点的树中插入数据项需要比较20或更少次的比较,再加上很短的时间来连接数据项.

同样,从有100w个数据项的数组中删除一个数据项需要平均移动50w个数据项,而在100w个节点的树中删除节点只需要20次或更少的比较来找到它,再加上(可能的话)一点比较的时间来找到它的后继,一点时间来断开这个节点的链接,以及连接它的后继.

结论: 树对所有常用的数据存储操作都有很高的效率

遍历不如其他操作快. 但是,遍历在大型数据库中不是常用的操作.它更长用于程序中的辅助方法来解析算术或其他的表达式,而且表达式一般都不会很长.

如果二叉树是平衡的,它的效率为: O(logN),如果二叉树是不平衡的(最极端的情况,存入树中的数据是升序或降序排列的,那么二叉树就是链表),效率为: O(N)

所以二叉搜索树在保存随机数值的时候,效率才是最高的

7. 二叉树的缺点

如果二叉树是极端不平衡的(此时的二叉树就是一个链表),它的效率为O(N),即使数值是随机的,如果数据的量够大,也有可能有一部分的数值是有序的(就像你抛硬币的时间足够长,会有一段时间出现一直抛正面或反面),造成二叉树会变成是局部不平衡的,这样它的效率会介于O(logN)到O(N).

END

精彩推荐

一百期Java面试题汇总

SpringBoot内容聚合

IntelliJ IDEA内容聚合

Mybatis内容聚合

我知道你 “在看”

涨姿势,简单易懂带你玩转二叉树(图码并茂)相关推荐

  1. 稀土掘金首页沸点课程直播活动竞赛商城APP邀请有礼插件探索稀土掘金搜索创作者中心vip会员4Java查序的头像一篇文章带你玩转二叉树的层序遍历 | 十道题巩固练习

    题目描述 解题思路 由题可知,要求输出是按照二叉树每层的元素来做输出 我使用队列来对二叉树每层元素进行存储和输出 根据队列的长度可以判断出当前层的元素个数并遍历 首先去判断入参是否为 null 如果为 ...

  2. canvas基础简单易懂教程(完结,多图)

    Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https ...

  3. 公式太多,读不懂? 一文带你领略KNN近邻算法~简单易懂

    ↑ 点击上方[计算机视觉联盟]关注我们 K近邻算法采用测量不同特征值之间的距离方法进行分类. K-近邻算法工作原理: 存在一个样本数据集合,也称作训练样本集,并且样本集中的每个数据都存在标签,即我们知 ...

  4. 计算机基础思维导图_超级简单的实操示范,教你零基础带孩子玩转风靡全球的思维导图...

    之前<妈妈是超人>播出时,朋友安利给我说黄圣依在这个节目里还挺圈粉的,于是我抽空专门找来看了看. 印象特别深刻的是,有一期安迪要参加一个主题为"炫耀妈妈"的演讲比赛,赛 ...

  5. 【转】区块链是什么,如何简单易懂地介绍区块链?

    作者:知乎用户 链接:https://www.zhihu.com/question/37290469 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 友情提醒:比特币 ...

  6. xbox手柄接收器驱动_新手有什么PC电脑手柄推荐?简单易懂5分钟教会你做出选择...

    不知道大家入手柄坑是在什么情况?我以前也经常疑惑,用手柄玩PC游戏是什么感觉,总是在想手柄好用吗?后台因为看着身边很多同学玩游戏都是用手柄在玩游戏,我就偷偷买回来试了试,结果发现,诶真香,也特别后悔, ...

  7. 由浅入深,带你玩转几种常用java设计模式

    由浅入深,带你玩转几种常用java设计模式 博客分类: 技术博客 课堂总结扩展 java设计模式单例工厂事件监听 PART A:前言 平常我们都在敲代码,为了要实现一些我们希望看到的功能,敲一大堆一大 ...

  8. cad的文字嵌入线条_带你玩转CAD!

    CAD画图已经成为化工人的必备技能.什么,这么多CAD必备技巧你居然还不知道?我该拿什么拯救你,我最最最最最最亲爱的旁友!!!下面给大家整理了50个相见恨晚的CAD技巧,带你玩转CAD!!相见恨晚的5 ...

  9. each 数据获取attr_调用高德POI数据,带你玩转长沙

    长沙CITY,长沙SHOW 长沙C-BLOCK,长沙FLOW 长沙妹驼,叫长沙GIRL 说到长沙,大家第一想到的可能就是小吃,当然来长沙旅游,不光只是为了吃,这吃喝玩乐,咱都得来一套是吧.基于此,我调 ...

最新文章

  1. 12c adg添加数据文件报错处理ORA-01111
  2. 发现一款绿色toolbar工具
  3. HTML基础:文本列表实例2(9)
  4. python wand安装_Python Wand posterize()用法及代码示例
  5. python_fullstack基础(十一)-常用模块
  6. Linux软件安装部署文档,MetaQ安装部署文档
  7. linux 找不到swap分区,Linux下swap分区没有UUID解决办法
  8. sql where 1=1 妙用之一方面
  9. redis存储对象_redis内存优化总结
  10. 如何下载:卫星地图高清2018,谷歌地图高清卫星地图,最新Google卫星地图
  11. 论文翻译:Few-Shot Object Detection with Attention-RPN and Multi-Relation Detector
  12. 修改Oracle系统管理员密码
  13. 悟空云课堂|第四十七期:会话固定(CWE-384: Session Fixation)
  14. 手把手教你用VMware安装Centos7.9镜像(史上最详细)
  15. 10岁自闭症小孩,妈妈带她几次粪菌移植治疗后,自闭行为有所改善
  16. Chrome浏览器更新
  17. js获取的当前时间的月份--getFullYear()、getMonth()、getDate(),本示例是获取当前月份的开始跟结束
  18. Pomotroid 使用指南:一款高颜值 PC 端番茄时钟
  19. 第五章-Linux实操篇
  20. JVM调优总结(十)-调优方法

热门文章

  1. 一加7T系列发布时间公布:9月26日北美和印度率先亮相
  2. 谷歌同意向法国支付近10亿美元罚款 以了结4年前的财务欺诈调查
  3. 华为Mate 30系列或下血本采用双主摄方案:CMOS尺寸破纪录
  4. 刘强东发新年信:过去一年我们异常艰难
  5. 个人简历(中英对照)词汇大全
  6. 晨哥真有料丨自信一点!恋爱做自己,不要自卑,不要迎合!
  7. 矩阵水平翻转java,通过翻转1的矩阵的行和列来确定是否可以到达给定二进制矩阵的算法...
  8. case when then else_每天一个常用MySQL函数-[case_when_then_end]
  9. 车辆行人检测数据集_澎思科技行人再识别技术取得突破,刷新三大数据集世界记录...
  10. echarts 3d饼图_Echarts 使用教程 1 基本使用方法