树堆

伪平衡二叉树的一种简单实现

Posted by ChaosNyaruko on February 21, 2021

树堆的基本概念

树堆(Treap)是二叉排序树(Binary Sort Tree)与堆(Heap)结合产生的一种拥有堆性质的二叉排序树。

但是这里要注意两点,第一点是Treap和二叉堆有一点不同,就是二叉堆必须是完全二叉树,而Treap并不一定是;第二点是Treap并不严格满足平衡二叉排序树(AVL树)的要求,即树堆中每个节点的左右子树高度之差的绝对值可能会超过1,只是近似满足平衡二叉排序树的性质。

Treap每个节点记录两个数据,一个是键值,一个是随机附加的优先级,Treap在以关键码构成二叉排序树的同时,又以结点优先级形成最大堆和最小堆。所以Treap必须满足这两个性质,一是二叉排序树的性质,二是堆的性质。如下图,即为一个树堆。 treap_demo

关键步骤

查找

按照正常BST的查找过程即可

插入

  • 给新节点一个随机的权重值,按照普通二叉搜索树,使用key进行插入
  • 为了保证堆的特性,插入完成后要进行旋转操作,保证仍为最大堆或最小堆。以最大堆为例,若插入到右子树,且权重比父结点大,就要实现一次左旋转,使右子树根结点成为新的父节点;反之左旋,直到满足堆的特性为止

删除

  • 按照正常的查找流程找到待删除节点后,有三种情况
    • 待删除结点本身就是叶子结点,直接删除即可
    • 待删除结点只有一个孩子(一棵子树),用这个孩子替换待删除结点,实现删除效果
    • 待删除结点两棵子树均不为空时,先进行旋转。以最大堆为例,找到权重更大的孩子,向相反方向旋转(即若左孩子权重更大,执行右旋),直到待删除结点满足上面两种情况之一,再进行删除

具体实现

参考LeetCode(cn) 第1438题的实现,其中对于旋转的优雅实现值得一学

import "math/rand"

type node struct {
    ch       [2]*node
    priority int
    key      int
    cnt      int
}

func (o *node) cmp(b int) int {
    switch {
    case b < o.key:
        return 0
    case b > o.key:
        return 1
    default:
        return -1
    }
}

func (o *node) rotate(d int) *node {
    x := o.ch[d^1]
    o.ch[d^1] = x.ch[d]
    x.ch[d] = o
    return x
}

type treap struct {
    root *node
}

func (t *treap) ins(o *node, key int) *node {
    if o == nil {
        return &node{priority: rand.Int(), key: key, cnt: 1}
    }
    if d := o.cmp(key); d >= 0 {
        o.ch[d] = t.ins(o.ch[d], key)
        if o.ch[d].priority > o.priority {
            o = o.rotate(d ^ 1)
        }
    } else {
        o.cnt++
    }
    return o
}

func (t *treap) del(o *node, key int) *node {
    if o == nil {
        return nil
    }
    if d := o.cmp(key); d >= 0 {
        o.ch[d] = t.del(o.ch[d], key)
    } else {
        if o.cnt > 1 {
            o.cnt--
        } else {
            if o.ch[1] == nil {
                return o.ch[0]
            }
            if o.ch[0] == nil {
                return o.ch[1]
            }
            d = 0
            if o.ch[0].priority > o.ch[1].priority {
                d = 1
            }
            o = o.rotate(d)
            o.ch[d] = t.del(o.ch[d], key)
        }
    }
    return o
}

func (t *treap) insert(key int) {
    t.root = t.ins(t.root, key)
}

func (t *treap) delete(key int) {
    t.root = t.del(t.root, key)
}

func (t *treap) min() (min *node) {
    for o := t.root; o != nil; o = o.ch[0] {
        min = o
    }
    return
}

func (t *treap) max() (max *node) {
    for o := t.root; o != nil; o = o.ch[1] {
        max = o
    }
    return
}

func longestSubarray(nums []int, limit int) (ans int) {
    t := &treap{}
    left := 0
    for right, v := range nums {
        t.insert(v)
        for t.max().key-t.min().key > limit {
            t.delete(nums[left])
            left++
        }
        ans = max(ans, right-left+1)
    }
    return
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

参考

树堆(Treap)图文详解与实现
leecode-cn 1438