树
数中的每一个元素称之为节点或结点,因为在英文中叫Node,翻译不同,所以俩个叫法都行
专业名词:
度:每一个节点的子节点数量
树高:树的总层数
根节点:最顶层的节点
左子节点:左下方的节点
右子节点:右下方的节点
根节点的左子树:其左半分支(别的节点也有子树)
根节点的右子树:其右半分支(别的节点也有子树)
每一个节点会存储:父节点地址、值、左子节点地址值、右子节点地址值
如果该节点没有父节点或者子节点,则其对应的地址值记为null
二叉树
二叉树:任意节点的度<=2称之为二叉树
普通的二叉树:
没有规律的存储数据,没有什么意义
二叉查找树
特点:
1.每一个节点上最多有两个子节点
2.任意节点左子树上的值都小于当前节点
3.任意节点右子树上的值都大于当前节点
添加
添加规则:
1.小的存左边
2.大的存右边
3.一样的不存
例如以下这组数据:7、10、4、5
1.首先存放7到根节点
2.判断10与7的关系,10 > 7所以10存放在7的右子节点
3.判断4与7的关系,4 < 7所以4存放在7的左子节点
4.判断5与7的关系,5 < 7所以5存放在7的左子节点,因为5的左子节点已经有4了,所以再次比较5与4的关系,5 > 4所以5存放在4的右子节点
查找
查找:
如果查找5:
1.让5与7进行比较,5 < 7所以查找7的左子树
2.让5与4比较,5 > 4所以查找4的右子树
3.此时找到5
如果查找12:
1.让12与7比较, 12 > 7所以查找7的右子树
2.让12与10比较,12 > 10所以查找10的右子树
3.让12与11比较,12 > 11所以查找11的右子树
4.此时找到12
如果查找30(二叉树中不存在的值):
1.让30与7比较, 30 > 7所以查找7的右子树
2.让30与10比较,30 > 10所以查找10的右子树
3.让30与11比较,30 > 11所以查找11的右子树
4.让30与12比较,30 > 12所以查找12的右子树
5.此时没有节点了,首先判定该二叉树中没有30这个值
二叉树的遍历方式
所有二叉树都能按照这个方式遍历
1.前序遍历
从根节点开始,然后按照当前节点,左子节点,右子节点的顺序遍历
例:
遍历顺序:20-18-16(此时没有左子节点)-19(此时没有右子节点)23-22-24
2.中序遍历
从最左边的子节点开始,然后按照左子节点,当前节点,右子节点(左-中-右)的顺序遍历
例:
1.20-18-16找到最左边的子节点16
2.开始遍历(左-中-右方式遍历):
16-18-19-20-22-23-24
特点:遍历出来的数据是从小到大的
3.后序遍历
从最左边的子节点开始,然后按照左子节点,右子节点,当前节点(左-右-中)的顺序遍历
例:
1.20-18-16找到最左边的子节点
2.开始遍历:
16-19-18-22-24-23-20
4.层序遍历
从根节点开始一层一层遍历
开始遍历:20-18-23-16-19-22-24
弊端
例如此时要存放7、10、11、12、13
此时根节点的左子树和右子树差的太多了,这样查询效率太低,此时就需要平衡二叉树
平衡二叉树
在二叉查找树的基础上保持二叉树的平衡
规则:任意节点左右子树高度差不不超过1
判断下方二叉树是否为平衡二叉树:
1.判断7的左子树高度为3,右子树高度为4,相差<=1
2.判断4的左子树高度为2,右子树高度为2,相差<=1
3.......
4.判断10的左子树高度为0,右子树高度为3,相差=3>1
所以答案:否
下面示范俩个是平衡二叉树的例子:
平衡二叉树是怎么让它们左右子树高度差不超过1?
通过旋转机制
旋转机制
规则1:左旋
规则2:右旋
触发时机:当添加一个节点时,该树不再是一个平衡二叉树
左旋
例:
此时下面这个二叉树它是一个平衡二叉树
如果添加一个12节点,那么此时就破坏了平衡,要开始旋转
旋转首先:
1.确定支点:从添加的节点开始,不断的往父节点去找,找到第一个不平衡的点,以它为支点
2.再以支点(此时为10)为基准,判断左旋还是右旋(此时10的右子树比左子树多,所以判断为左旋操作)
步骤:
1.以不平衡的点作为支点
2.把支点左旋降级,变成左子节点
3.晋升原来的右子节点
此时再来看一种相对复杂的情况,建议在上上张图的基础上先增加9节点再增加12节点:
1.确定支点:从12开始找,找到支点为7
步骤:
1.以不平衡的点为支点
2.将根节点的右侧往左拉
3.原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级了的根节点当作右子节点
右旋
例:
参考左旋,结果:
需要旋转的四种情况
1.左左
2.左右
3.右右
4.右左
左左
当根节点左子树的左子树有节点插入,导致二叉树不平衡
理解,例:
此时根节点的左子树就是4、2、5这三个节点,这个左子树的左子树就是2这个节点
在2这个节点插入节点(图1-2)导致二叉树不平衡(图1-2,此时节点7左右子树的高度差=2>1)
1-1
1-2
旋转:
右旋:
此时恢复平衡
结论:左左导致的不平衡,一次右旋就能解决
左右
当根节点的左子树的右子树有节点插入,导致的不平衡
理解:例:
此时根节点的左子树就是4、2、5三个节点组成的子树,这个子树的右子树为5
在5这个节点插入节点(图2-2)导致二叉树不平衡(图2-2,此时7的左右子树层数=2>1)
2-1
2-2
错误示范
旋转:
右旋:此时依旧不平衡(4的左右子树层数=2>1)
此时高度差依旧为2,表面左右不能通过一次右旋达成平衡二叉树
正确示范:
旋转:
局部左旋:先将4、2、5、6这块子树左旋,将左右的情况变成左左
右旋:
此时恢复平衡
结论:左右导致的不平衡,先局部左旋,再整体右旋
右右
当根节点右子树的右子树有节点插入,导致二叉树不平衡
理解:例:
此时根节点的右子树为10、9、11,该子树的右子树为11,此时添加节点12(图3-2),导致二叉树不平衡(7的左右子树层数差=2>1)
3-1
3-2
旋转:
左旋:
此时恢复平衡
结论:右右导致的不平衡,一次左旋就能解决
右左
当根节点的右子树的左子树有节点插入,导致二叉树不平衡
理解,例:
此时根节点的右子树为10、9、11,该子树的左子树为9
此时添加节点8(图4-1)导致二叉树不平衡(4-2,根节点左右子树层数差=2>1)
4-1
4-2
错误示范:
旋转:
左旋:
此时高度差依旧为2,表面右左不能通过一次左旋达成平衡二叉树
正确示范:
旋转:
局部左旋:将右左变成右右
整体左旋:
此时恢复平衡
结论:右左导致的不平衡,先局部左旋,再整体左旋
弊端
由于添加节点需要旋转,会造成添加节点的时间浪费
红黑树
红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构
1972年出现,当时被称之为平衡二叉B树。后来,1978年被修改为如今的“红黑树”
它是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色
每一个节点可以是红或者黑;红黑树不是高度平衡的,它的平衡是通过“红黑规则”进行实现的
红黑树每个节点中存储:父节点地址值、值、左子节点地址值、右子节点地址值、颜色(新增)
平衡二叉树和红黑树对比:
红黑树: 平衡二叉树:
是一个二叉查找树 高度平衡
但是不是高度平衡的 当左右子树高度差超过1时,通过旋转保持平衡
条件:特有的红黑规则
红黑规则
1.每一个节点或是红色的,或是黑色的
2.根节点必须是黑色
3.如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
4.如果某一个节点是红色,那么它的子节点必须是黑色(不能出现俩个红色节点相连的情况)
5.对每一个节点,从该节点到其所有后代(该节点的所有子树节点)叶节点(Nil节点)的简单路径(例如下图13-8-11-Nil就是一条简单路径,核心:不能往回走)上,均包含相同数目的黑色节点
例:下图就是一个红黑树
存储:
此时拿6这个节点举例,其内存中存储了父节点地址值,6,左和右子节点地址值为空,颜色为红
添加规则
默认颜色:添加节点默认是红色的(效率高)
为什么效率高?
例如此时要添加20、18、23三个节点,且默认为黑色的话
首先添加20:满足红黑规则
再添加18:此时就已经违背了红黑规则第五条
需要将18改成红色,才能满足红黑规则:
再添加23:此时又违背了红黑规则第五条
需要将23变成红色,才能满足红黑规则:
结论:如果默认添加元素是黑色,那么添加三个节点需要调整两次
现在演示这三个节点默认为红色的情况:
首先添加23:此时违背红黑规则第二条
需要将20变成黑色,才能满足红黑规则:
再添加18节点:满足红黑规则
再添加23节点:满足红黑规则
结论:添加三个节点,只需要调整一次,所以默认颜色为红色效率更高
除了默认颜色外,红黑树还有很多添加节点的规则
例:
此时需要添加20、18、23、22、17、25、19、15、14/16
1.首先添加20:此时违反红黑规则第二条
解决方案:将20改为黑色,则满足红黑规则
2.添加18:满足红黑规则(非根-父黑色-不需要操作)
3.添加23:满足红黑规则(非根-父黑色-不需要操作)
4.添加22:违背红黑规则第四条
解决方案:非根-父红色-叔叔红色(此时父为23-叔叔为18)
1.将父变成黑、叔叔变成黑。
2.将祖父设为红色
3.如果祖父为根,再将祖父设为黑(true)
如果祖父非根,将祖父设置为当前节点再进行其他判断(false)
5.添加17:满足红黑规则(非根-父黑色-不需要操作)
6.添加24和19时,同上述5(非根-父黑色-不需要操作)
7.添加15节点:此时违反红黑规则第四条
解决方案:非根-父红色-叔叔红色(此时父为17,叔叔为19)
1.将父变成黑、叔叔变成黑。
2.将祖父设为红色
3.如果祖父为根,再将祖父设为黑(false)
如果祖父非根,将祖父设置为当前节点再进行其他判断(true)
以18为当前节点判断(非根-父黑色-不需要操作)
8.添加14节点:违背红黑规则第四条
解决方案:非根-父红色-叔叔黑色-当前节点为父节点的左子节点(此时父为15,叔叔为Nil)
1.将父设为黑色
2.将祖父设为红色
3.以祖父为支点进行右旋
9.假设没有添加8步骤中的14,而是添加16:此时违背红黑规则第四条
解决方案:非根-父红色-叔叔黑色-当前节点是父节点的右子节点(此时父为15,叔叔为Nil)
1.把父作为当前节点并左旋,再进行判断
此时15为当前节点(非根-父红色-叔叔红色-当前节点为父节点的左子节点)
1.将父设为黑色
2.将祖父设为红色
3.以祖父为支点进行右旋
此时就彻底解决!!!
总结
红黑树增删改查的性能都很好