一篇文章教你搞懂二叉树的方方面面
对于二叉树,可以说是相当重要的数据结构了,但又不是难度很大
基本概念
-
树的相关术语
根节点:顾名思义,最上面那个一枝独秀的。
叶节点:下面没有更多节点了的节点。
节点的度:节点含有的子树的个数。
树的度:所有节点中的最大的度。
子节点:父节点的儿子。。。
父节点:子节点的父亲。。。
兄弟节点:子节点的兄弟,同父。。。
这些概念我觉得没什么必要,主要你了解这个树的数据结构就好了,学这么多名词干什么。 -
二叉树
顾名思义,有两个叉的树,也就是一个父节点最多两个儿子。
满二叉树:每一层的节点树都达到最大值 -
完全二叉树:相当重要的概念,叶节点只出现在最下层和次下层,并且最下面的一层的 节点都集中在左边的若干位置。
如何理解?
就是插入必须是层序的,一层完了才能有下一层,而且必须从左往右,不能中间跳过。
二叉查找树BST(binary search)
首先,二叉查找树并不一定是完全二叉树。
BST的特点在于,== 当前节点的左子节点小于当前节点,而右子节点大于当前节点 ==,
对于节点,我们可以只插入值,也可以插入键值对。
好了,接下来看一下如何实现BST吧。
首先定义树的结构,root为根节点,N为节点个数,节点具有的属性为Key,Value,left左子节点,right右子节点。
接着定义树的api,主要有以下几个api:
插入 put()
这里用到了递归的思想,因为BST的构造规则是左中右依次变大,那么对于要插入的节点,首先判断root是否为空,要是空,那没啥事了,直接插入成为root即可
不为空的话,开始比较大小了,要是比root小,那么往左走,反之往右走,如果找到相同的key值,那么就需要替换了。关于递归的出口,那就是当当前节点为null,这时候就该插入新节点了,说的我也有点晕,画个图吧。
获取get()
get 方法可谓和put一脉相承,原理是一样的。
删除
删除很简单,但删除后仍然要保证BST的结构有效,是应该考虑的问题
思路是这样的,删除一个节点之后,从这个节点的右子树里面找出最小的元素替代该节点,能够保证BST的结构。
但是这样做,有一个缺陷,例如下面这种情况
假设我要删除4节点,我们按照之前的算法试一下会发生什么。
首先找到了4之后,应该找到右子树中的最小节点,那就是7了
可是在删除的时候,7是没有左子节点的,那么是完成不了删除操作的。
那么需要再加一段判断,直接让minNode替换掉要删除的节点x即可。
我想说的是,尽管如此,二叉树的删除还有其他的情况没有考虑,程序肯定是不完善的。这也和二叉树他的不平衡有很大关系,同样的数据插入的顺序不同就会造成不同的二叉树,导致之后的操作有很多的不确定性。
遍历(老生常谈)
思想也是递归的思想,code看一下就明白了。
首先这里自己定义了一个Queue来进行遍历。
先序,中序和后序遍历
很容易懂
关于他们之间的转换问题,也可以参考我的另一篇博客
传送门—>>>二叉树不同遍历的相互转换
折纸问题(层序遍历)
层序遍历指从上往下,从左往右依次遍历二叉树
也是递归的思想,判断每一个节点的左右是否存在,入队,每次操作的时候出队头结点,然后把头结点直接入队到最终的keys队列里面,等到queue中没有节点了,那么keys中也就按照层序遍历入队成功了。
接下来说一说折纸问题,这算是层序遍历的应用吧。
根据需求其实我们可以发现,把折痕抽象成树的节点,左子节点都为下折痕down,右子节点都为上折痕up,这是解决问题的关键。
本文参考了黑马程序员的相关教程
传送门—>>>源码地址