二叉搜索树BST

Posted cold-cold

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉搜索树BST相关的知识,希望对你有一定的参考价值。

  二叉搜索树(Binary Search Tree)它要么是一棵空树,要么是一棵具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左,右子树也分别为二叉搜索树

  二叉搜索树上的每一个结点都维护四个值,一个是它本身的权值data,有siz表示当前点为根的子树大小,ls记录左儿子的编号,rs记录右儿子的编号

1. 插入节点(insert)

插入节点的时候从根节点出发

(1) 如果当前节点为空,则将data赋为我们要插入的节点,siz初始化为1,再将其父亲连向他(这里我们要注意,二叉搜索树,是不记录父节点的,为了能使其父亲连向他,我们每次调用insert函数的时候我们传入其父亲指向其子节点的地址,而我们要使其父亲指向它,就是将该地址上的值改为新节点的编号)

(2) 如果要插入的节点比我们当前的节点小,就向该节点的左子树前进,同时,将该节点的siz加1(因为要插入的节点一定是插入到该节点的子树中去了)

(3) 如果要插入的节点比我们当前的节点大等于我们当前的节点,就向该节点的右子树前进,同时,将该节点的siz加1(因为要插入的节点一定是插入到该节点的子树中去了)

是不是很容易呀,实现如下:

void insert(int &now,int v)
{
    if(!now)
    {
        data[++tot]=v;
        siz[tot]=1;
        now=tot;
        return;
    }
    ++siz[now];
    if(data[now]<=v) insert(rs[now],v);
    else insert(ls[now],v);
}

2. 删除节点(del)

其实删除操作非常奇怪,我们不可以直接删除一个有两个子节点的节点,我们从根节点出发

(1) 如果当前节点为空就返回

(2) 如果要插入的节点比我们当前的节点小,就向该节点的左子树前进,同时,将该节点的siz加1(因为要插入的节点一定是插入到该节点的子树中去了)

(3) 如果要插入的节点比我们当前的节点大,就向该节点的右子树前进,同时,将该节点的siz加1(因为要插入的节点一定是插入到该节点的子树中去了)

(4) 如果要插入的节点等于我们当前的节点

a) 如果该点只有一个儿子,就直接将该点的父亲连向该点的儿子,连接方法如上

b) 不然,我们就找到比该点大的最小点,将两点交换,再删除比该点大的最小点

是不是很容易呀,实现如下:

void del(int &now,int v)
{
    if(!now) return;
    --siz[now];
    if(data[now]==v)
    {
        if(!ls[now]||!rs[now]) now=ls[now]+rs[now];
        else
        {
            int temp=rs[now];
            while(ls[temp]) temp=ls[temp];
            data[now]=data[temp];
            del(rs[now],data[temp]);
        }
    }
    else if(data[now]<=v) del(rs[now],v);
    else del(ls[now],v);
}

 3. 查询某数排名(rank)

我们从根节点出发

  (1) 如果当前节点为空就返回1

(2) 如果要插入的节点比我们当前的节点小,就向该节点的左子树继续查询

(3) 如果要插入的节点比我们当前的节点大,就向该节点的右子树继续查询,同时将查询得到的值+siz+1后返回

  其实很容易写错,实现如下:

int rank(int &now,int v)
{
    if(!now) return 1;
    if(data[now]<v) return siz[ls[now]]+1+rank(rs[now],v);
    else return rank(ls[now],v);
}

4. 查询第k大的数(kth)

我们从根节点出发

(1) 如果当前节点为空就返回-1(找不到)

(2) 如果要查的排名等于siz[ls[now]]+1,就返回该节点

(3) 如果要插入的节点比我们当前的节点小,就向该节点的左子树继续查询

(4) 如果要插入的节点比我们当前的节点大,就向该节点的右子树继续查询排名为k-siz-1的数

其实很容易写错,实现如下:

int kth(int &now,int v)
{
    if(!now) return -1;
    if(siz[ls[now]]+1==v) return data[now];
    else if(siz[ls[now]]<v) return kth(rs[now],v-1-siz[ls[now]]);
    else return kth(ls[now],v);
}

5. 求某数的前驱(前驱定义为小于该数,且最大的数)(pred)

我们从根节点出发

(1) 如果当前节点为空就返回-0x3f3f3f3f

(2) 如果要插入的节点比我们当前的节点小或等于我们当前的节点,就向该节点的左子树继续查询

(3) 如果要插入的节点比我们当前的节点大,就向该节点的右子树继续查询,并将查询得到的值与该点取最大值返回

  其实很容易写错,实现如下

int pred(int &now,int v)
{
    if(!now) return -0x3f3f3f3f;
    if(data[now]<v) return max(data[now],pred(rs[now],v));
    else return pred(ls[now],v);
}

6. 求某数的后继(后继定义为大于该数,且最小的数)(succ)

我们从根节点出发

(1) 如果当前节点为空就返回0x3f3f3f3f

(2) 如果要插入的节点比我们当前的节点小,就向该节点的左子树继续查询,并将查询得到的值与该点取最小值返回

(3) 如果要插入的节点比我们当前的节点大或等于我们当前的节点,就向该节点的右子树继续查询

  其实很容易写错,实现如下:

int succ(int &now,int v)
{
    if(!now) return 0x3f3f3f3f;
    if(data[now]<=v) return succ(rs[now],v);
    else return min(data[now],succ(ls[now],v));
}

是不是很容易呀,完整代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
inline int read()
{
    int a=0,q=0;
    char ch=getchar();
    while((ch<0||ch>9)&&ch!=-) ch=getchar();
    if(ch==-)  q=1,ch=getchar();
    while(ch>=0&&ch<=9) a=(a<<3)+(a<<1)+ch-48,ch=getchar();
    return q?-a:a;
}
const int N=100100;
int tot(0),root(0),siz[N],ls[N],rs[N],data[N],op,x,n;
void insert(int &now,int v)
{
    if(!now)
    {
        data[++tot]=v;
        siz[tot]=1;
        now=tot;
        return;
    }
    ++siz[now];
    if(data[now]<=v) insert(rs[now],v);
    else insert(ls[now],v);
}
void del(int &now,int v)
{
    if(!now) return;
    --siz[now];
    if(data[now]==v)
    {
        if(!ls[now]||!rs[now]) now=ls[now]+rs[now];
        else
        {
            int temp=rs[now];
            while(ls[temp]) temp=ls[temp];
            data[now]=data[temp];
            del(rs[now],data[temp]);
        }
    }
    else if(data[now]<=v) del(rs[now],v);
    else del(ls[now],v);
}
int rank(int &now,int v)
{
    if(!now) return 1;
    if(data[now]<v) return siz[ls[now]]+1+rank(rs[now],v);
    else return rank(ls[now],v);
}
int kth(int &now,int v)
{
    if(!now) return -1;
    if(siz[ls[now]]+1==v) return data[now];
    else if(siz[ls[now]]<v) return kth(rs[now],v-1-siz[ls[now]]);
    else return kth(ls[now],v);
}
int pred(int &now,int v)
{
    if(!now) return -0x3f3f3f3f;
    if(data[now]<v) return max(data[now],pred(rs[now],v));
    else return pred(ls[now],v);
}
int succ(int &now,int v)
{
    if(!now) return 0x3f3f3f3f;
    if(data[now]<=v) return succ(rs[now],v);
    else return min(data[now],succ(ls[now],v));
}
int main()
{
    // freopen("data","r",stdin);
    // freopen("output","w",stdout);
    n=read();
    while(n--)
    {
        scanf("%d%d",&op,&x);
        if(op==1) insert(root,x);
        else if(op==2) del(root,x);
        else if(op==3) printf("%d
",rank(root,x));
        else if(op==4) printf("%d
",kth(root,x));
        else if(op==5) printf("%d
",pred(root,x));
        else printf("%d
",succ(root,x));
    }
    return 0;
}
/*

*/

 

以上是关于二叉搜索树BST的主要内容,如果未能解决你的问题,请参考以下文章

C++ 实现二叉排序树(搜索树BST)(完整代码)

树--05---二叉树--02---二叉搜索树(BST)遍历

二叉搜索树(BST)---python实现

python实现二叉搜索树_二叉搜索树(BST)---python实现

树--04---二叉树--01---简介二叉搜索树(BST)实现

[leetcode]450. Delete Node in a BST二叉搜索树删除节点