二叉搜索树与平衡二叉树(详解,含C++代码)

目录

 

一.引言:

二.Binary search tree

三.Balance Tree

四.代码实现

一.引言:

当需要写一种数据结构,来维护一些数

需要提供一下操作

  • 插入x数
  • 删除x数
  • 查询x数的排名
  • 查询排位为x的数
  • 求x的前驱(小于x的最大数)
  • 求x的后继(大于x的最小数)

与我们学过的线代树做比较发现:最复杂的线段树也没有改变树的顺序

所以核心就是动态维护有序序列

二.Binary search tree

BST按照中序遍历(左,根,右)就是从小到大的有序序列

插入

删除

找BST的最大最小值:最大值一直往右走,最小值一直往左走。

找前驱:中序遍历中的前一个位置

操作:如果存在左子树,则左子树的最大值就是前驱,如果不存在左子树找第一个右儿子的父节点。

找后继:中序遍历中的后一个位置

比x大与等于的最小值*lower_bound(x)

找比x的大的最小值*upper_bound(x)

!!!以上的操作set已经实现好了

insert()  erase()  iterator++,iterator--   , begin() , end()

*lower_bound(x)    *upper_bound(x)

出现以上操作不需要手写BST,用set就可以解决,set的底层实现是红黑树


  • 查询x数的排名
  • 查询排位为x的数
  • 比某个数小的最大值

这两个操作set解决不了

三.Balance Tree

 

 

 常见的平衡树有:AVL树,树堆(Treap),伸展树(Splay tree),红黑树,B树,B+树等

常见的应用于大数据库索引。

树Tree +  堆Heap  == Treap!!

 首先我们回忆一下堆

 堆是一颗完全二叉树,那就满足了”平衡“这个要求!!

但是堆不保证左子节点的值小于本身小于右子节点,所以怎么解决使其称为一个BST?

Hint : 一个随机的BST它的期望高度是logN

所以我们不根据原数据的大小建立堆,因为不能保证BST

不过,我们可以给每个节点再赋一个随机权值,根据这个随机权值构建堆

所以我们写一下每个节点

struct Node{
	int l,r;   //左孩子和右孩子的编号
	int key;   //BST中的值
	int val;  //大根堆中的权值(随机值)
}tr[N];

此时就可以唯一确定一颗BST了,每一步选择子树根的方式是唯一的。

根节点为val的最大值,然后根据根节点的key把剩余节点分为大于key和小于key,子树依次递归

首先在初始化的时候加入两个哨兵-inf和+inf处理边界问题

如何维护呢?   

插入很简单,二叉树的插入(一直递归到叶子结点就行)

但是如何进行上调维护平衡呢?  左旋,右旋

对A结点进行左旋与右旋操作:

 

 

 

 

 

 

 旋转性质

左旋:左子树的高度+1,右子树的高度-1

右旋:右子树的高度+1,左子树的高度-1

旋转前是BST,旋转后还是BST

如果是左儿子就右旋,右儿子就左旋,直到所在位置满足大根堆

删除操作:通过左旋和右旋操作把要删除的结点转移到叶子结点删除,每次旋转把val大的儿子旋转上来维护大根堆的性质,左孩子大右旋,右孩子大左旋。

总结:通过左旋和右旋维护堆和BST的性质

四.代码实现

节点

struct Node
{
    int l, r;
    int key, val;
    int cnt, size;  //size为子树的节点数目
}tr[N];

更新size

void pushup(int p)
{
    tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + tr[p].cnt;
}

创建新节点

int get_node(int key)
{
    tr[ ++ idx].key = key;
    tr[idx].val = rand();
    tr[idx].cnt = tr[idx].size = 1;
    return idx;
}

右旋

void zig(int &p)    //右旋
{
    int q = tr[p].l;
    tr[p].l = tr[q].r, tr[q].r = p, p = q;
    pushup(tr[p].r), pushup(p);
}

左旋

void zag(int &p)    // 左旋
{
    int q = tr[p].r;
    tr[p].r = tr[q].l, tr[q].l = p, p = q;
    pushup(tr[p].l), pushup(p);
}

初始化

void build()
{
    get_node(-INF), get_node(INF);
    root = 1, tr[1].r = 2;
    pushup(root);

    if (tr[1].val < tr[2].val) zag(root);
}

插入

void insert(int &p, int key)
{
    if (!p) p = get_node(key);
    else if (tr[p].key == key) tr[p].cnt ++ ;
    else if (tr[p].key > key)
    {
        insert(tr[p].l, key);
        if (tr[tr[p].l].val > tr[p].val) zig(p);
    }
    else
    {
        insert(tr[p].r, key);
        if (tr[tr[p].r].val > tr[p].val) zag(p);
    }
    pushup(p);
}

删除

void remove(int &p, int key)
{
    if (!p) return;
    if (tr[p].key == key)
    {
        if (tr[p].cnt > 1) tr[p].cnt -- ;
        else if (tr[p].l || tr[p].r)
        {
            if (!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val)
            {
                zig(p);
                remove(tr[p].r, key);
            }
            else
            {
                zag(p);
                remove(tr[p].l, key);
            }
        }
        else p = 0;
    }
    else if (tr[p].key > key) remove(tr[p].l, key);
    else remove(tr[p].r, key);

    pushup(p);
}

通过数值找排名

int get_rank_by_key(int p, int key)    // 通过数值找排名
{
    if (!p) return 0;   // 不存在key
    if (tr[p].key == key) return tr[tr[p].l].size + 1;
    if (tr[p].key > key) return get_rank_by_key(tr[p].l, key);
    return tr[tr[p].l].size + tr[p].cnt + get_rank_by_key(tr[p].r, key);
}

通过排名找数值

int get_key_by_rank(int p, int rank)   // 通过排名找数值
{
    if (!p) return INF;    
    if (tr[tr[p].l].size >= rank) return get_key_by_rank(tr[p].l, rank);
    if (tr[tr[p].l].size + tr[p].cnt >= rank) return tr[p].key;
    return get_key_by_rank(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt);
}

找到严格小于key的最大数

int get_prev(int p, int key)   // 找到严格小于key的最大数
{
    if (!p) return -INF;
    if (tr[p].key >= key) return get_prev(tr[p].l, key);
    return max(tr[p].key, get_prev(tr[p].r, key));
}

找到严格大于key的最小数

int get_next(int p, int key)    // 找到严格大于key的最小数
{
    if (!p) return INF;
    if (tr[p].key <= key) return get_next(tr[p].r, key);
    return min(tr[p].key, get_next(tr[p].l, key));
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值