一、二叉搜索树特点
1.1、简介
一棵二叉树是结点的一个有限集合,该集合或者为空,或者由一个根结点加上两棵左子树和右子树组成,二叉树的特点就是左子树的结点值小,
而右子树的结点值比父结点大。可以采取类似于二分查找的思想,正常情况下快速的找到这个结点。
1.2、重点操作
对于树中的每个结点X,它的左子树中所有关键字的值小于X的关键字值,而它的右子树中所有关键字值都大于X的关键字值
根据这个特性,对一个二叉树进行中序遍历,如果时单调递增的,则可以说明这个数二叉搜索树重点操作有插入、删除
1.2.1、插入:
1、从根结点开始,遇键值较大则向左走,与键值较小则向右走,直至尾部,即插入点
1.2.2、删除:
1、如果存在于叶子结点,直接删除即可
2、删除的结点只有一个子结点,则将子结点连至父结点的即可(可以视为将子结点替换掉删除结点的位置)
3、删除的结点有两个子结点,这时需将右子树的最小值替换掉删除结点即可,最小结点可通过删除结点右子树一直向左走到底获得,
二叉树:
树中每个结点最多有两个子树。不排序。
完全二叉树:
对于深度为K,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一 一对应时称之为完全二叉树。
满二叉树:
除最后一层无任何子结点外,每一层上的所有结点都有两个子结点的二叉树。
一个二叉树,如果每一个层的结点数都达到最大值,则这个树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数为2^k-1,则它
是满二叉树。
二、平衡二叉搜索树
2.1、简介
平衡二叉树,又称AVL树指的是左子树上的所有结点的值都比根结点的值小,而右子树上的所有结点的值都比根结点的值大,且任意一个结点左
子树与右子树的高度差最大为1。
2.2、重点操作
发生的情况:当对二叉树进行插入或者删除的时候,有可能造成二叉搜索树失去平衡,造成搜寻效率低落的情况。
2.2.1、插入:
当向二叉搜索树插入一个结点分为ie四种情况
1、插入结点位于X的左子结点的左子树--左左
2、插入结点位于X的左子结点的右子树--左右
3、插入结点位于X的右子结点的左子树--右左
4、插入结点位于X的右子结点的右子树--右右
情况1、4彼此对称,称为外侧插入,采用单旋转操作即可调整
情况2、3彼此对成,称为内侧插入,可以采用双旋转(执行两次单旋转)操作调整。双旋转如下:
插入结点的祖父节点旋转一次,新的祖父节点的父节点再旋转一次。
2.2.2 删除
1、被删除结点没有儿子,即为叶结点。
1、将该结点直接从树中删除;
2、其父结点的子树高度的变化将导致父结点平衡因子的变化,通过向上检索并推算其父结点是否失衡;
3、如果其父结点未失衡,则继续向上检索推算其父结点的父结点是否失衡…如此反复②的判断,直到根结点;如果向上推算过程中发
现了失衡的现象,则进行④的处理;
4、如果其父结点失衡,则判断是哪种失衡类型[LL、LR、RR、RL],并对其进行相应的平衡化处理。如果平衡化处理结束后,发现与
原来以父结点为根结点的树的高度发生变化,则继续进行②的检索推算;如果与原来以父结点为根结点的高度一致时,则可说明父结点
的父结点及祖先结点的平衡因子将不会有变化,因此可以退出处理。
我们将上图中节点8删除,得到下图:
在左子树上删除节点其实就相当于在右子树上插入节点。但要如何调整还取决于该失衡节点的右孩子的左右子树的高度差。我们找到节点
20的父节点的右子树上30,发现节点30的左右子树高度一样,因此我们只需进行RR型调整,也就是左旋一次,根据父节点的另一个子节点30进
行逆时针旋转。调整后的树如下:
2、被删除结点只有一个儿子。
1、那么,直接删除该结点,并用该结点的唯一子结点顶替它的位置。
2、以删除结点的父结点为推算点,依此向上检索推算各结点(父、祖先)是否失衡;
3、如果其父结点未失衡,则继续向上检索推算其父结点的父结点是否失衡,如果有失衡则进行步骤4
4、判断是哪种失衡类型[LL、LR、RR、RL],并对其进行相应的平衡化处理。如果平衡化处理结束后,发现与原来以父结点为根结点
的树的高度发生变化,则继续进行②的检索推算;如果与原来以父结点为根结点的高度一致时,则可说明父结点的父结点及祖先结点的
平衡因子将不会有变化,因此可以退出处理。
这可以用上面的例子,只是删除的节点变成了20。
3、 被删除结点有两个儿子。
1、① 如果该结点的平衡因子为0或者1,则找到其左子树中具有最大值的结点max(我们只讨论有序平衡二叉树,并且有序平衡二叉树
中任意一个结点的左子树上的所有结点的值小于该结点的值,右子树上所有结点的值大于该结点的值),将max的内容与x的内容交换
(只替换保存的真正的数据,不替换指针,平衡因子等用于管理目的的信息),并且max即为新的要删除的结点。由于树是有序的,因
而这样找到的结点要么是一个叶子结点,要么是一个没有右子树的结点。
2、如果该结点的平衡因子为-1,则找到其右结点中具有最小值的结点min,将min的内容与x的内容交换,并且min即为新的要删除的节
点。由于树是有序的,因而这样找到的结点要么是一个叶子结点,要么是一个没有左子树的结点
通过上面的操作后,我们需要对值替换后的结点进行删除,根据树的性质,我们可以得到我们要输出的结点只有两种情况,叶子结点或
者是只有一棵子树的结点。删除后的调整操作按照我们前面已经讲的两种方法去调整。
2.3、输入一棵二叉树的根结点,求树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树深度。
1、利用递归的思想,对于每个节点,判断它的左子树深度为多少,判断它的右子树深度为多少
2、将左子树深度与右子树深度中最大的深度+1,即为该节点的深度
/* 实现:求二叉树深度的函数TreeDepth */ int TreeDepth(TreeNode* pRoot) { if(pRoot==NULL){ return 0; } int leftLen = TreeDepth(pRoot->left); int rightLen= TreeDepth(pRoot->right); return leftLen>rightLen ? (leftLen+1) : (rightLen+1); }
判断是否是平衡二叉树 /* 实现:判断是否是平衡二叉树 */ //简单版,但是存在不足,就是会遍历很多重复的节点 bool IsBalanced(BinaryTreeNode* pRoot){ if (pRoot == NULL){ return true; } int leftLen = TreeDepth(pRoot->left); int rightLen = TreeDepth(pRoot->right); int diff = leftLen - rightLen; if (diff > 1 || diff < -1){ return false; } return IsBalanced(pRoot->left) && IsBalanced(pRoot->right); } //利用后序遍历,这样对每个节点只需要遍历一次 bool IsBalanced(BinaryTreeNode* pRoot,int* depth){ if (pRoot == NULL){ *depth = 0; return true; } if (IsBalanced(pRoot->left, &leftLen) && IsBalanced(pRoot->right, &rightLen)){ int diff = leftLen - rightLen; if (diff <= 1 || diff >= -1){ *depth = leftLen > rightLen ? leftLen + 1 : rightLen + 1; return true; } } return false; }
三、红黑树
3.1、简介
红黑树是一种自平衡二叉查找树,红黑树放弃追求完全平衡,追求大致平衡,与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多
只需要三次选择就能达到平衡,实现起来也更为简单。平衡二叉树追求完全平衡,条件比较苛刻,实现起来比较麻烦,每次插入新节点之后需要旋转
的次数不能预知。
红黑树不仅是一个二叉搜索树,还必须满足以下规则:
1、每个结点不是红色就是黑色
2、根结点一定为黑色
3、每个叶子结点都是黑色的空结点(NIL结点)。//这里叶子结点,是指为空的叶子结点!
4、如果结点为红色,则子结点必须为黑色 //红之子、之父比黑 插入、删除会影响这个规则
5、任一结点至NULL(树尾端)的任何路径,所含之黑结点树必须相同。//外部结点到根:途中经过黑结点数目相同
红黑树是主要通过性质五来约束树的高度,提高树的平衡性。因为从该结点到其子孙结点的所有路径上包含相同数目的黑结点,所以新插入
的结点需要是红结点来保证黑结点数目相等。又通过性质四来维护树的平衡性。
3.2、插入情况及解决方案:
插入步骤:
第一步: 将红黑树当作一颗二叉查找树,将结点插入。
第二步:将插入的结点着色为"红色",插入为红色,不会违背,不会违背"特性(5)"!少违背一条特性。
第三步: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。
核心思想:将红色的结点移到根结点;然后,将根结点设为黑色,先解决新插入结点引发的颜色问题,再解决到叶子结点经过黑结点数量问题。
1、空树
方案:检测是否是根结点,如果是,将颜色改为黑
2、父亲结点p是黑色
方案:不会破坏红黑树的性质,不需要改变
3、父结点P红色,祖父结点G黑色,叔结点U红色
方案:祖父结点必然是黑色,已经不满足红黑树的性质了。
将当前结点的父结点和叔叔结点涂黑,祖父结点涂红,把当前结点指向祖父结点(递归平衡,进入新的情况)
1、父亲涂黑:解决孩子是红的问题,产生包含‘父结点’的分支的黑色结点的总数增加了1的问题,
2、祖父涂红:为了解决上述问题,所以将祖父涂红。但产生了新的问题:“包含‘叔叔结点’的分支的黑色结点的总数较少1”。
3、叔叔涂黑:能解决““包含‘叔叔结点’的分支的黑色结点的总数减少1的问题, 同时着 “包含‘祖父结点’的分支的黑色结点的总数增加了1。
4、父结点P红色,祖父结点为黑色,叔结点为黑色,且当前结点是其父结点的右孩子
方案:以新当前结点的父结点左旋,当前结点的父结点做为新的当前结点(递归平衡,进入新的情况)
1、左旋:要不断的将破坏红黑树特性的红色结点上移(即向根方向移动),若旋转后的结点变成了根结点,那么直接将其设为“黑色”,就完全
解决问题了
2、父结点做为新的当前结点:如选择后该结点不是根结点,则将父结点做为新的当前结点,因为他已经从父结点变为了子结点,从而破坏
红黑树的性质。处理问题的时候,需要从下至上(由叶到根)方向进行处理。
5、父结点P红色,祖父结点为黑色,叔结点为黑色,且当前结点是其父结点的左孩子
方案:父结点变为黑色,祖父结点变为红色,在祖父结点为支点右旋。
1、父结点变黑:解决孩子是红的问题,产生包含‘父结点’的分支的黑色结点的总数增加了1的问题。
2、祖父结点变为红色并旋转:解决包含‘父结点’的分支的黑色结点的总数增加了1的问题。
3.3、删除情况及解决方案
在删除节点后,原红黑树的性质可能被改变,如果删除的是红色节点,那么原红黑树的性质依旧保持,此时不用做修正操作,如果删除的节点是
黑色节点,原红黑树的性质可能会被改变,我们要对其做修正操作。那么哪些树的性质会发生变化呢,如果删除节点不是树唯一节点,那么删除节点
的那一个支的到各叶节点的黑色节点数会发生变化,此时性质5被破坏。如果被删节点的唯一非空子节点是红色,而被删节点的父节点也是红色,那
么性质4被破坏。如果被删节点是根节点,而它的唯一非空子节点是红色,则删除后新根节点将变成红色,违背性质2。”
第一步:将红黑树当作一颗二叉查找树,将结点删除。
1、被删除结点没有儿子,即为叶结点。那么,直接将该结点删除就OK了。
2、被删除结点只有一个儿子。那么,直接删除该结点,并用该结点的唯一子结点顶替它的位置。
3、 被删除结点有两个儿子。那么,先找出它的后继结点;然后把“它的后继结点的内容”复制给“该结点的内容”;之后,删除“它的后继节
点”。在这里,后继结点相当于替身,在将后继结点的内容复制给"被删除结点"之后,再将后继结点删除。这样就巧妙的将问题转换为"删除
后继结点"的情况了,下面就考虑后继结点。 在"被删除结点"有两个非空子结点的情况下,它的后继结点不可能是双子非空。既然"后继节
点"不可能双子都非空,就意味着"该结点的后继结点"要么没有儿子,要么只有一个儿子。若没有儿子,则按"情况① "进行处理;若只有一个
儿子,则按"情况② "进行处理。
第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
因为"第一步"中删除结点之后,可能会违背红黑树的特性。所以需要通过"旋转和重新着色"来修正该树,使之重新成为一棵红黑树。
修复的情况有点复杂:下面我们用一个分析技巧:我们从被删结点后来顶替它的那个结点开始调整,并认为它有额外的一重黑色。这里额外一重
黑色是什么意思呢,我们不是把红黑树的结点加上除红与黑的另一种颜色,这里只是一种假设,我们认为我们当前指向它,因此空有额外一种黑色,
可以认为它的黑色是从它的父结点被删除后继承给它的,它现在可以容纳两种颜色,如果它原来是红色,那么现在是红+黑,如果原来是黑色,那么
它现在的颜色是黑+黑。有了这重额外的黑色,原红黑树性质5就能保持不变。现在只要恢复其它性质就可以了,做法还是尽量向根移动和穷举所有可
能性。
如果当前节点是红+红,不会影响性质,直接替换就行。
如果是以下情况,恢复比较简单:另为了方便说明将当前结点设为x。
1、情况说明:当前结点是红+黑色
解法,直接把当前结点染成黑色,结束此时红黑树性质全部恢复。
2、情况说明:当前结点是黑+黑且是根结点
解法:什么都不做,结束。
3、情况说明:当前结点是“黑+黑”结点,且不是根。那么有一条路径将会少了一个黑节点。
1、删除修复情况1:当前结点x是黑+黑且兄弟结点为红色(此时父结点和兄弟结点的子结点分为黑),即处理是否需要将右节点变为黑色。
(01) 将x的兄弟结点设为“黑色”。
(02) 将x的父结点设为“红色”。
(03) 对x的父结点进行左旋。
(04) 左旋后,重新设置x为兄弟结点。
变化前:
变化后:
2、删除修复情况2:当前结点是黑加黑且兄弟是黑色且兄弟结点的两个子结点全为黑色
(01) 将x的兄弟结点设为“红色”。
(02) 设置“x的父结点”为“新的x结点”,重新进入算法。
变化前:
变化后:
3、删除修复情况3:当前结点颜色是黑+黑,兄弟结点是黑色,兄弟的左子是红色,右子是黑色
(01) 将x兄弟结点的左孩子设为“黑色”。
(02) 将x兄弟结点设为“红色”。
(03) 对x的兄弟结点进行右旋。
(04) 右旋后,重新设置x的兄弟结点,之后重新进入算法,即将当前情况转化为情况4
变化前:
变化后:
4、删除修复情况4:当前结点颜色是黑-黑色,它的兄弟结点是黑色,但是兄弟结点的右子是红色,兄弟结点左子的颜色为任意色。
(01) 将x父结点颜色 赋值给 x的兄弟结点。
(02) 将x父结点设为“黑色”。
(03) 将x兄弟结点的右子节设为“黑色”。
(04) 对x的父结点进行左旋。
(05) 设置“x”为“根结点”。
变化前:
变化后:
红黑树参考连接:https://blog.csdn.net/v_JULY_v/article/details/6105630
本文来自博客园,作者:快牵着我的袜子,转载请注明原文链接:https://www.cnblogs.com/socks/p/12768003.html