学习笔记_树(数据结构)

学习笔记_树

哨兵 NIL

哨兵sentinel是一个哑dummy对象, 可以用来简化边界条件;
可以用在环形双向链表中, nil介于头和尾之间

二叉查找树

注意点

  1. 根节点是树中唯一父节点为nil的节点

特性

  1. 如果一个节点y是节点x的左子树,则key[y] < key[x]
  2. 如果一个节点y是节点x的右子树,则key[y] > key[x]

遍历-中序遍历

获得的查询结果是递增的

INORDER-TREE-WALK(x) 
if x != nil
    then INORDER-TREE-WALK(left[x]) 
        printf(x)
        then INORDER-TREE-WALK(left[x]) 

查找

ITERATIVE-TREE-SEARCH(x, k) 
while x!=nil or k!=key[x]
    do if k < key[x]
        then x = left[x]
        else x = right[x]
return x

最小值

TREE-MINIIMUM(x) 
while left[x] != nil
        else x = left[x]
return x

最大值

TREE-MAXIMUM(x) 
while right[x] != nil
        else x = right[x]
return x

后继

给定一个节点,找出中序遍历下它的后继
TREE-subccessor

if right[x] != nil                  //如果x有右子树,则查找右子树的最小值
    then TREE-MINIMUN(right[x])
y = p[x]                            //如果x是左子树,则返回p
while y != nil and x == right[y]    //否则一直会找到根节点
    do x = y
       y = p[y]
return y

前趋

给定一个节点,找出中序遍历下它的前趋
TREE-predecessor

y = p[x]                            //如果x是左子树,则返回p
while y != nil and x == right[y]    //否则一直会找到根节点
    do x = y
       y = p[y]
return y

插入

TREE-INSERT(T,z)
y = nil
x = root[T]
while x != nil
    do  y = x
        if key[z] < key[x]
            then x = left[x]
            else x = right[x]
p[z] = y                        // 查找下来x=nil的位置即为要插入的位置
if y == nil
    then root[T] = z            //说明是一个空树
    else if key[z] < key[y]     
            then left[y] = z
            else right[y] = z

删除

  1. Z有0个子女, 则p[z] = nil
  2. z有1个子女, 则将z的父和子相连来将z删除
  3. z有2个子女, 先删除z的后继y, 再用y的内容来替代z的内容
TREE-DELETE(T,z)
    if left[z] == nil or right[z] == nil
        then y = z
        else y = TREE-SUCCESSOR(z)          // 1-3行确定要删除的节点y,是z还是z后继
    if left[y] != nil
        then x = left[y]
        else x = right[y]                   // 4-6行x被置为y的非nil子女(都nil则nil)
    if x == nil
        then p[x] = p[y]
    if p[y] == nil
        then root[T] == x
        else if y == left[p[y]]
                then left[p[y]] = x
        else right[p[y]] = x               // 7-13行,通过修改p[y]和x的指针将y删除
    if y != z
        then key[z] = keyp[y]
            "copy z's satellite data into z"//如果要删除的是z的后继,则将后继数据复制到z中
    return y;                               // 返回要删除的节点y

红黑树

目的

确保没有一条路径会比其他路径长出两倍, 因而接近平衡
为了平衡黑色的高度, 以减少检索的深度, 左右相差不超过2倍
对于红黑树的恢复, 是将红色节点不断向根节点移动, 然后将根节点变为黑色

特性

  1. 每个节点是红的或者黑的
  2. 根节点是黑色的
  3. 每个叶节点(nil)是黑色的
  4. 如果一个节点是红色的,则它的两个儿子都是黑色的
  5. 对每个节点,从该节点到其子孙节点的所有路径上包含相同数目的黑节点( 黑高 一样_)

哨兵的使用

采用一个哨兵nil[T]来代表所有的NIL, 以节省空间和简化逻辑

左旋

指针结构的修改是用过旋转来完成的, 这是一种能保持二叉查找树性质的查找树局部操作.
假设x可以是树内任意右孩子不是nil[T]的节点,
在节点x上左旋, 让y为x的右孩子
左旋以x到y之间的链为"支轴"进行
使y称为该子树新的根, x称为y的左孩子, y的左孩子称为x的右孩子

  1. y = x->right
  2. x.right -> y.left
  3. y.parent -> x.parent
  4. x.parent -> y.parent
LEFT-ROTATE(T,x)
    y = right[x]
    right[x] = right[y] 
    p[left[y]] = x                      //将x的右子树指向y的左子树
    p[y] = p[x]                     
    if p[x] == nil[T]
        then root[T] = y
        else if x == left[p[x]]
                then left[p[x]] = y
                else right[p[x]] = y    // 将x的父指针指向y
    left[y] = x
    p[x] = y                            // 将y的左子树指向x

右旋, 节点的左边向右旋转

将左旋的x改成y, left改成right, right改成left

插入

RBTREE-INSERT(T,z)
y = nil[T]
x = root[T]
while x != nil[T]
    do  y = x
        if key[z] < key[x]
            then x = left[x]
            else x = right[x]
p[z] = y                        // 查找下来x=nil的位置即为要插入的位置
if y == nil[T]
    then root[T] = z            //说明是一个空树
    else if key[z] < key[y]     
            then left[y] = z
            else right[y] = z
left[z] = nil[T]
right[z] = nil[T]
color[z] = RED
RB-INSERT-FIXUP(T, z)

破坏了特性2(根节点必须为黑色)和性质4(红节点不能右红子女)
// 父节点是红色, 则祖父节点是黑色的,叔父节点是红黑之一
// 1.叔父节点是红色的
// 2.叔父节点是黑色的,当前节点是父亲节点的左节点
// 3.叔父节点是黑色的,当前节点是父亲节点的右节点

RB-INSERT-FIXUP(T, z)
    while color[p[z]] == RED                        // 父节点是红色, 则祖父节点是黑色的,叔父节点是红黑之一
        do 
        if p[z] == left[p[p[z]]]
            then y = right[p[p[z]]]                 // 获取叔父节点,先判断父节点是左的情况
                 if color[y] == RED                 // 情况1,如果叔父节点是红色的
                    then color[p[z]] = BLCAK        // 情况1
                         color[y] = BLACK           // 情况1
                         color[p[p[z]]] = RED       // 情况1
                         z = p[p[z]]                // 情况1
                else 
                    if z == right[p[z]]
                        then z = p[z]           // 情况2,叔父节点黑色,当前节点左
                             LEFT-ROTATE(T,z)   // 情况2
                    color[p[z]] = BLACK         // 情况3,叔父节点黑色,当前节点右
                    color[p[p[z]]] = RED        // 情况3
                    RIGHT-ROTATE(T, p[p[z]])    // 情况3
        else (same as clause with "right" and "left" exchanged)
color[root[T]] = BLACK

删除

  1. Z有0个子女, 则p[z] = nil
  2. z有1个子女, 则将z的父和子相连来将z删除
  3. z有2个子女, 先删除z的后继y, 再用y的内容来替代z的内容
RBTREE-DELETE(T,z)
    if left[z] == nil[T] or right[z] == nil[T]
        then y = z
        else y = TREE-SUCCESSOR(z)          // 1-3行确定要删除的节点y,是z还是z后继
    if left[y] != nil[T]
        then x = left[y]
        else x = right[y]                   // 4-6行x被置为y的非nil子女(都nil则nil)
    p[x] = p[y]
    if p[y] == nil[T]
        then root[T] == x
        else if y == left[p[y]]
                then left[p[y]] = x
                else right[p[y]] = x               // 7-13行,通过修改p[y]和x的指针将y删除
    if y != z
        then key[z] = keyp[y]
            "copy z's satellite data into z"//如果要删除的是z的后继,则将后继数据复制到z中
    if(color[y]=BLACK)
        then RB-DELETE-FIXUP(T, x)
    return y                               // 返回要删除的节点y

B树

对于3阶, 则每个节点最多存放2个数据, 最多具有3个子树
根据 Knuth 的定义,一个 m 阶的B树是一个有以下属性的树:

  1. 每一个节点最多有 m 个子节点
  2. 每一个非叶子节点(除根节点)最少有 ⌈m/2⌉ 个子节点
  3. 如果根节点不是叶子节点,那么它至少有两个子节点
  4. 有 k 个子节点的非叶子节点拥有 k − 1 个键
  5. 所有的叶子节点都在同一层
    如果一个内部节点有3个子节点(子树),那么它就必须有两个键: a1 和 a2 。左边子树的所有值都必须小于 a1 ,中间子树的所有值都必须在 a1 和a2 之间,右边子树的所有值都必须大于 a2 。

2-3-4树(4阶B树)

B+树(中间节点只保存索引,所有数据保存在叶子节点)

一个m阶的B+树具有如下几个特征:

  1. 有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。
  2. 所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
  3. 所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。

学习链接

<<算法导论>>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值