二叉搜索树(BST)

二叉查找树

引入:

平衡树的基础就是二叉寻找树。

首先看一下这个二叉树:

假如说查询第 \(n\) 大的值,那么排序时间复杂度爆炸。

如果我们用上这棵树,就可以在 \(O(\log n)\) 的时间内查找,并且 \(O(\log n)\) 修改,\(O(1)\) 连边。

实现过程:

函数定义:

struct node{
    int sizes,val,cnt; int ls,rs;
    //rank表示排名,cnt表示当前节点代表的数有几个.
    //sizes表示当前节点的子树大小和自己的大小的和
}t[N];

插入:

一句话总结:就是大的往右,小的往左,直到该节点没有儿子,就新生一个儿子节点赋值。

代码:

void add(int x,int v){
    t[x].sizes++;
    if(t[x].val==v){t[x].cnt++; return;}
    if(t[x].val>v){
        if(t[x].ls!=0) add(t[x].ls,v);
        else bnt++,t[bnt].val=v,t[bnt].sizes=t[bnt].cnt=1,t[x].ls=bnt;
    }
    else{
        if(t[x].rs!=0) add(t[x].rs,v);
        else bnt++,t[bnt].val=v,t[bnt].sizes=t[bnt].cnt=1,t[x].rs=bnt;
    }
}

删除:

这个图的删除可以用到 \(cnt\) 标记,找到这个数后 \(cnt-1\) 即可,如果 \(cnt=0\) ,就说明没有这个数。

查询:

支持的查询有很多种:找前驱,找后继,按值查排名和按排名查值

前驱后继:

定义为小于/大于 \(x\) 的最大的数。

总结一下,就是从根节点开始,如果该节点 大于/小于 要找的数,就找其 左子树/右子树 ,否则找他的 右子树/左子树 ,直到不能找。

代码:

int getpre(int x,int val,int ans){
    if(t[x].val>=val){
        if(t[x].ls==0) return ans;
        else return getpre(t[x].ls,val,ans);
    }
    else
        if(t[x].rs==0){
            if(t[x].val<val) return t[x].val;
            else return ans;
        }
    if(t[x].cnt!=0) return getpre(t[x].rs,val,t[x].val);
    else return getpre(t[x].rs,val,ans);
}
int getnxt(int x,int val,int ans){
    if(t[x].val<=val){
        if(t[x].rs==0) return ans;
        else return getpre(t[x].rs,val,ans);
    }
    else
        if(t[x].ls==0){
            if(t[x].val>val) return t[x].val;
            else return ans;
        }
    if(t[x].cnt!=0) return getpre(t[x].ls,val,t[x].val);
    else return getpre(t[x].ls,val,ans);
}

按排名找值:

使用 \(sizes\) ,根据 \(BST\) 的性质,右边的元素严格大于左边的元素,所以右面的排名也大于左面的,排名为 \(n\) 的数就是第 \(n\) 靠左的节点。

我们在按排名查值时,当前位置的排名为他左子树的大小加上自己 \(cnt\)(该节点有几个)的大小,如果当前排名小于要找的排名,就去右子树找,并更新要找的排名,反之先自查,不行再去左子树找.

int getval(int x,int rank){//按排名找值
    if(x==0) return 0x3f3f3f3f;
    if(t[t[x].ls].sizes>=rank) return getval(t[x].ls,rank);
    if(t[t[x].ls].sizes+t[x].cnt>=rank) return t[x].val;
    return getval(t[x].rs,rank-t[t[x].ls].sizes-t[x].cnt);
}

按值找排名:

总结一下,按值找排名时,从根开始,如果该位置的值小于要查询的值,就找他的右子树,同时记住他左子树的大小,如果小于,就查询他的左子树,直到大小相等,他的排名就是该点左子树的大小加上一路上比他小的节点个数再加上 \(1\).

int getrank(int x,int val){
    if(x==0) return 0x3f3f3f3f;
    if(val==t[x].val) return t[t[x].ls].sizes+1;
    if(val<t[x].val) return getrank(t[x].ls,val);
    return getrank(t[x].rs,val)+t[t[x].ls].sizes+t[x].cnt;
}

本文主要内容借鉴ztz11 的博客

posted @ 2021-08-11 19:42  Evitagen  阅读(173)  评论(0)    收藏  举报