二叉搜索树

二叉搜索树

总定义

  1. 若左子树不空,则左子树上所有结点的值均小于它的根结点的值。
  2. 若右子树不空,则右子树上所有结点的值均大于它的根结点的值。
  3. 左、右子树也分别为二叉搜索树。
  4. 没有权值相等的结点

结点定义

  • 当前节点的权值 val

    • 即序列中的数
  • 左孩子的下标与右孩子的下标 ls rs

  • 计数器 cnt

    • 代表当前的值出现了几遍
  • 子树的小和该结点大小之和 siz

  • struct node{
    	int val,ls,rs,cnt,siz;
    }tree[500010];
    

操作实现

插入

\(x\) 是当前节点的下标,\(v\) 是要插入的值。

要在树上插入一个 \(v\) 的值,就要找到一个合适 \(v\) 的位置。

如果本身树的节点内有代表 \(v\) 的值的节点,就把该节点的计数器加 \(1\)

否则一直向下寻找,直到找到叶子节点,这个时候就可以从这个叶子节点连出一个儿子,代表 \(v\) 的节点。具体向下寻找该走左儿子还是右儿子是根据二叉搜索树的性质来的。

void add(int x,int v)
{
	tree[x].siz++;
	//如果查到这个节点,说明这个节点的子树里面肯定是有v的,所以siz++
	if(tree[x].val==v)
    {
		//如果恰好有重复的数,就把cnt++,退出即可,因为我们要满足第四条性质
		tree[x].cnt++;
		return ;
	}
	if(tree[x].val>v)//如果v<tree[x].val,说明v实在x的左子树里
    {
		if(tree[x].ls!=0)
        {
			add(tree[x].ls,v);//如果x有左子树,就去x的左子树
        }
		else//如果不是,v就是x的左子树的权值
        {
			cont++;//cont是目前BST一共有几个节点
			tree[cont].val=v;
			tree[cont].siz=tree[cont].cnt=1;
			tree[x].ls=cont;
		}
	}
	else{//右子树同理
		if(tree[x].rs!=0)
        {
            add(tree[x].rs,v);
		}
		else
        {
			cont++;
			tree[cont].val=v;
			tree[cont].siz=tree[cont].cnt=1;
			tree[x].rs=cont;
		}
	}
}

找前驱

\(x\) 是当前节点的下标,\(val\) 是要找前驱的值,\(ans\) 是目前找到的比 \(val\) 小的数的最大值。

int queryFr(int x, int val, int ans) {
	if (tree[x].val>=val)
	{//如果当前值大于val,就说明查的数大了,所以要往左子树找
		if (tree[x].ls==0)//如果没有左子树就直接返回找到的ans
			return ans;
		else//如果不是的话,去查左子树
			return queryFr(tree[x].ls,val,ans);
	}
	else
	{//如果当前值小于val,就说明我们找比val小的了
		if (tree[x].rs==0)//如果没有右孩子,就返回tree[x].val,因为走到这一步时,我们后找到的一定比先找到的大(参考第二条性质)
			return (tree[x].val<val) ? tree[x].val : ans
		//如果有右孩子,,我们还要找这个节点的右子树,因为万一右子树有比当前节点还大并且小于要找的val的话,ans需要更新
		if (tree[x].cnt!=0)//如果当前节数的个数不为0,ans就可以更新为tree[x].val
			return queryFr(tree[x].rs,val,tree[x].val);
		else//反之ans不需要更新
			return queryFr(tree[x].rs,val,ans);
	}
}

找后继

int queryNe(int x, int val, int ans) {
	if (tree[x].val<=val)
	{
		if (tree[x].rs==0)
			return ans;
		else
			return queryNe(tree[x].rs,val,ans);
	}
	else
	{
		if (tree[x].ls==0)
			return (tree[x].val>val)? tree[x].val : ans;
		if (tree[x].cnt!=0)
			return queryNe(tree[x].ls,val,tree[x].val);
		else
			return queryNe(tree[x].ls,val,ans);
	}
}

按值找排名

这里我们就要用到 \(siz\) 了,排名就是比这个值要小的数的个数再 \(+1\),所以我们按值找排名。

int queryVal(int x,int val)
{
	if(x==0) return 0;//没有排名 
	if(val==tree[x].val) return tree[tree[x].ls].siz;
	//如果当前节点值=val,则我们加上现在比val小的数的个数,也就是它左子树的大小 
	if(val<tree[x].val) return queryVal(tree[x].ls,val);
	//如果当前节点值比val大了,我们就去它的左子树找val,因为左子树的节点值一定是小的 
	return queryVal(tree[x].rs,val)+tree[tree[x].ls].siz+tree[x].cnt;
	//如果当前节点值比val小了,我们就去它的右子树找val,同时加上左子树的大小和这个节点的值出现次数 
	//因为这个节点的值小于val,这个节点的左子树的各个节点的值一定也小于val 
}
//注:这里最终返回的是排名-1,也就是比val小的数的个数,在输出的时候记得+1

按排名找值

由性质 \(1.\) \(2.\) ,我们发现排名为 \(n\) 的数在 BST 上是第 \(n\) 靠左的数。

或者说排名为 \(n\) 的数的结点在 BST 中,它的左子树的 \(siz\) 与它的各个祖先的左子树的 \(siz\) 相加恰好等于 \(n\) (减去重复部分)。

由此问题转化。

int queryRk(int x,int rk)
{
	if(x==0) return INF; 
	if(tree[tree[x].ls].siz>=rk)//如果左子树大小>=rk了,就说明答案在左子树里 
		return queryRk(tree[x].ls,rk);//查左子树 
	if(tree[tree[x].ls].siz+tree[x].cnt>=rk)//如果左子树大小加上当前的数的多少恰好>=k,说明我们找到答案了 
		return tree[x].val;//直接返回权值 
	return queryRk(tree[x].rs,rk-tree[tree[x].ls].siz-tree[x].cnt);
	//否则就查右子树,同时减去当前节点的次数与左子树的大小 
}

删除

具体就是利用二叉搜索树的性质在树上向下爬找到具体节点,把计数器-1。与上文同理就不粘贴代码了

P5076普通二叉树

上述板子

#include<iostream>
#include<cstdio>
#define re register
using namespace std;
const int INF=0x7fffffff;
int cont;

struct node{
    int val,siz,cnt,ls,rs;
}tree[1000010];

int n,opt,xx;

inline void add(int x,int v)
{
    tree[x].siz++;
    if(tree[x].val==v)
	{
        tree[x].cnt++;
        return ;
    }
    if(tree[x].val>v)
	{
        if(tree[x].ls!=0)
          	add(tree[x].ls,v);
        else
		{
            cont++;
            tree[cont].val = v;
            tree[cont].siz = tree[cont].cnt = 1;
            tree[x].ls = cont;
        }
    }
    else{
        if(tree[x].rs!=0)
          	add(tree[x].rs,v);
        else
		{
            cont++;
            tree[cont].val = v;
            tree[cont].siz = tree[cont].cnt=1;
            tree[x].rs = cont;
        }
    }
}

int queryfr(int x, int val, int ans) {
    if (tree[x].val>=val)
    {
        if (tree[x].ls==0)
            return ans;
        else
            return queryfr(tree[x].ls,val,ans);
    }
    else
    {
        if (tree[x].rs==0)
            return tree[x].val;
        return queryfr(tree[x].rs,val,tree[x].val);
    }
}
int queryne(int x, int val, int ans) {
    if (tree[x].val<=val)
    {
        if (tree[x].rs==0)
            return ans;
        else
            return queryne(tree[x].rs,val,ans);
    }
    else
    {
        if (tree[x].ls==0)
            return tree[x].val;
        return queryne(tree[x].ls,val,tree[x].val);
    }
}

int queryrk(int x,int rk)
{
    if(x==0) return INF;
    if(tree[tree[x].ls].siz>=rk)
        return queryrk(tree[x].ls,rk);
    if(tree[tree[x].ls].siz+tree[x].cnt>=rk)
        return tree[x].val;
    return queryrk(tree[x].rs,rk-tree[tree[x].ls].siz-tree[x].cnt);
}

int queryval(int x,int val)
{
    if(x==0) return 0;
    if(val==tree[x].val) return tree[tree[x].ls].siz;
    if(val<tree[x].val) return queryval(tree[x].ls,val);
    return queryval(tree[x].rs,val)+tree[tree[x].ls].siz+tree[x].cnt;
}

inline int read()
{
    re int r=0;
    re char ch=getchar();
    while(ch<'0'||ch>'9')
        ch=getchar();
    while(ch>='0'&&ch<='9'){
        r=(r<<3)+(r<<1)+(ch^48);
        ch=getchar();
    }
    return r;
}

signed main()
{
    n=read();
    while(n--)
	{
        opt=read();xx=read();
        if(opt==1) printf("%d\n",queryval(1,xx)+1);
        else if(opt==2) printf("%d\n",queryrk(1,xx));
        else if(opt==3) 
		{
			if (cont == 0)
			{
				cout << -2147483647 << endl;
			}
			else
				printf("%d\n",queryfr(1,xx,-INF));
		}
        else if(opt==4) printf("%d\n",queryne(1,xx,INF));
        else{
            if(cont==0){
                cont++;
                tree[cont].cnt=tree[cont].siz=1;
                tree[cont].val=xx;
            }
            else add(1,xx);
        }
    }
    return 0;
}

深基板子

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define MAXN 100010

int n, root, cnt, opt, x;

struct Node
{
	int left, right, size, value, num;
	Node(int l, int r, int s, int v)
		: left(l), right(r), size(s), value(v), num(1) {}
	Node() {}
} t[MAXN];

void update(int root)
{
	t[root].size = t[t[root].left].size + t[t[root].right].size + t[root].num;
}

int queryRank(int x, int root)
{
	if (root)
	{
		if (x < t[root].value)
		{
			return queryRank(x, t[root].left);
		}
		if (x > t[root].value)
		{
			return queryRank(x, t[root].right) + t[t[root].left].size + t[root].num;
		}
		if (x == t[root].value)
		{
			return t[t[root].left].size + t[root].num;
		}
	}
	return 1;
}

int queryVal(int x, int root)
{
	if (x == 0)
	{
		return -0x7fffffff;
	}
	if (x <= t[t[root].left].size)
	{
		return queryVal(x, t[root].left);
	}
	if (x <= t[t[root].left].size + t[root].num)
	{
		return t[root].value;
	}
	return queryVal(x - t[t[root].left].size - t[root].num, t[root].right);
}

void insert(int x, int &root)
{
	if (x < t[root].value)
	{
		if (!t[root].left)
		{
			t[root].left = ++cnt;
			t[cnt] = Node(0, 0, 1, x);
		}
		else
		{
			insert(x, t[root].left);
		}
	}
	else if (x > t[root].value)
	{
		if (!t[root].right)
		{
			t[root].right = ++cnt;
			t[cnt] = Node(0, 0, 1, x);
		}
		else
		{
			insert(x, t[root].right);
		}
	}
	else
	{
		t[root].num++;
	}
	update(root);
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin >> n;
	int num = 0;
	t[root = ++cnt] = Node(0, 0, 1, 2147483647);
	while (n--)
	{
		cin >> opt >> x;
		num++;
		switch (opt)
		{
			case 1:
				cout << queryRank(x, root) << endl;
				break;
			case 2:
				cout << queryVal(x, root) << endl;
				break;
			case 3:
				cout << queryVal(queryRank(x, root) - 1, root) << endl;
				break;
			case 4:
				cout << queryVal(queryRank(x + 1, root), root) << endl;
				break;
			case 5:
				num--;
				insert(x, root);
				break;
		}
	}
	return 0;
}
posted @ 2023-11-24 15:21  加固文明幻景  阅读(6)  评论(0编辑  收藏  举报