数据结构与算法-学习笔记(18)

平衡二叉查找树

平衡二叉查找树的严格定义:二叉树中任意一个节点的左右子树的高度相差不能大于1。 bash

设计平衡二叉查找树的初衷:为了解决普通二叉查找树在频繁的插入、删除等动态更新的状况下(尤为是插入的一组数据是有序的状况),出现时间复杂度退化的问题。数据结构

可是在实际的开发中,并不能彻底严格的按照定义去作,只要保证平衡便可,也就是让整棵树左右看起来比较对称、比较平衡、更新数据时不要形成一边很高、一边又很低的状况。这样就能让整棵树的高度相对来讲低一些(如树的高度是对数量级的,没必要log2n大不少)。只要能保证这样,就能够说它是一个合格的平衡二叉查找树。性能

平衡二叉查找树-红黑树(R-B Tree)

红黑树中的节点,一类被标记为黑色,一类被标记为红色。而且要求:spa

    1. 每一个节点不是黑色就是红色,根节点必须是黑色的(能够在节点数据结构中加一个数据字段表明颜色);
    1. 每一个叶子节点都是黑色的空节点(nil)(为了保证除了根之外,每一个节点都有兄弟节点,也就是每一个父节点都有两个叉),也就是说叶子节点不存储数据;
    1. 任何相邻(线连着)的节点都不能同时为红色,也就是说,红色节点是被黑色节点隔开的;
    1. 每一个节点,从该节点到达其可达叶子节点的全部路径,都包含相同数目的黑色节点(说明黑色节点是要成对出现的,不然失去平衡了)。

为何说红黑树是“近似平衡的”

近似平衡就是说查找等动态更新的性能不会退化太严重。设计

二叉查找树不少操做的性能都很树的高度成正比,所以只要证实红黑树的高度能比较稳定的趋近log2n就好。3d

去掉红色节点的黑色节点(无父节点则祖父节点代替,这样防止本来黑色节点的高度发生改变)可能变成3叉树/4叉树。他们要比相同节点数相同的彻底二叉树的高度要小,因此它的高度不超过log2n。

而红色节点必须是被黑色节点隔开的,一红一黑,所以红色节点加回去,树的高度应该不大于2log2n。高度仅是大了一倍,性能上降低很少,能够近似O(logn)。code

同时红黑树是近似平衡,所以维护平衡的成本较低,性能比较稳定。对于工程应用来讲,要面对各类异常状况,稳定性很重要,所以红黑树的应用范围较广。cdn

动态数据结构

动态数据结构是指支持动态的更新操做,里面存储的数据是时刻在变化的,它支持查询、删除、插入等操做,而且这些操做是很是高效。这样的数据结构才能算做动态数据结构。blog

如何维护红黑树的近似平衡

红黑树规定插入的节点必须是红色的且在修改过程当中不能改变颜色,由于插入红色节点比黑色节点违背规则的可能性更小。插入黑色节点必定会改变黑色高度(违背规则4),而插入红色只有一半机会违背规则3。且3比4更易修正。开发

当插入一个新节点(二叉查找树插入新节点会插到叶子节点上,那么在红黑树上也就是插入到黑色空节点上层了)就可能会破坏红黑树本来结构,打破平衡,那么如何修正呢?

主要有三种方式:改变节点颜色、左旋、右旋。

  1. 变色 假设本来只有节点E,而后插入了节点A和S,再插入F:

  2. 左旋 一般左旋操做用于将一个向右倾斜的红色连接转为向左连接。

    动图效果:

  3. 左旋

动图效果:

红黑树的操做

  1. 节点数据
typedef struct TreeNode {
    int key;
    struct TreeNode *left;
    struct TreeNode *right;
    struct TreeNode *father; // 双向链表
    BOOL color;
    
} Node;
复制代码
  1. 左旋的实现
/*
 * 左旋示意图:对节点x进行左旋
 *     p                       p
 *    /                       /
 *   x                       y
 *  / \                     / \
 * lx  y      ----->       x  ry
 *    / \                 / \
 *   ly ry               lx ly
 * 左旋作了三件事:
 * 1. 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时)
 * 2. 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右)
 * 3. 将y的左子节点设为x,将x的父节点设为y
 */
复制代码
  1. 右旋的实现
/*
 * 左旋示意图:对节点y进行右旋
 *        p                   p
 *       /                   /
 *      y                   x
 *     / \                 / \
 *    x  ry   ----->      lx  y
 *   / \                     / \
 * lx  rx                   rx ry
 * 右旋作了三件事:
 * 1. 将x的右子节点赋给y的左子节点,并将y赋给x右子节点的父节点(x右子节点非空时)
 * 2. 将y的父节点p(非空时)赋给x的父节点,同时更新p的子节点为x(左或右)
 * 3. 将x的右子节点设为y,将y的父节点设为x
 */
复制代码
  1. 插入操做
  • 若是第一次插入,本来树是空的,则只须要将它涂黑便可。
  • 若是最终插入后,插入节点的父节点是黑色的,则不违反红黑树规则,不需改变。
  • 若是最终插入后,插入节点的父节点是红色的,就违反规则了,须要修改保持树的平衡了。此时又有三种状况:
    • ①插入节点的父节点和其叔叔节点(祖父节点的另外一个子节点)均为红色的;
    • ②插入节点的父节点是红色,叔叔节点是黑色,且插入节点是其父节点的右子节点;
    • ③插入节点的父节点是红色,叔叔节点是黑色,且插入节点是其父节点的左子节点。

具体过程为例: 插入新节点4,如图1(也就是状况①)。此时违反3,所以将它的父节点涂黑,可是若是只涂5,就会形成黑色节点不平衡了,违反了4。所以新节点的父节点5和叔叔节点8都要涂黑。这时758都是黑色(黑色太多了?)将祖父7涂红。这时又变成状况②,把7当作新节点 ,以新节点 为支点作左旋操做。如图2->3。此时27节点仍有问题继续修改,此时那2做为新节点,将父节点7涂黑,祖父节点11涂红。在以11为支点右旋。这样整棵树就又平衡。

从上面的步骤能够看出,若是插入数据最终是状况1,则须要走完二、3步骤;若是插入数据出现的是状况2,则只需走完3;插入后是3,则完成3便可了。

整体:变色->左旋->右旋

资料

相关文章
相关标签/搜索