八、数据结构(二叉树的应用)

现在我们需要写一种数据结构来维护一些数(都是 1 0 9 10^9 109 以内的数字)的集合,最开始时集合是空的。其中需要提供以下操作,操作次数 q q q 不超过 10000 10000 10000

  1. 查询x数的排名(排名定义为比当前数小的数的个数 + 1 +1 +1。若有多个相同的数,应输出最小的排名)。
  2. 查询排名为x的数。
  3. 查询x的前驱和后继
  4. 插入一个数 x。
    对于这个需求,我们可以使用暴力枚举的方法,也可以使用二叉搜索树完成。

二叉搜索树的性质

二叉搜索树具有以下的性质:

  1. 若结点x的左子树不空,则x左子树中所有结点的值均小于结点x的值。
  2. 若结点x的右子树不空,则x右子树中所有结点的值均大于结点x的值。
  3. 任意结点的左、右子树也分别是二叉搜索树。
  4. 没有键值相等的结点。
    下面是一棵二叉搜索树

节点设计

可以对每个结点定义5个变量:

  1. 用left 表示左儿子
  2. right表示右儿子
  3. value 表示该结点的权值
  4. size 表示以该结点为根结点的子树的结点个数
  5. num 表示该结点权值出现的次数。

功能设计

  1. 查询 x x x 数的排名:
    每次将 x x x 和根结点 r o o t root root 的权值比较,如果 x x x 小于根结点 r o o t root root 的权值,那么 r o o t root root 的右子树里面的所有权值都比 x x x 要大,所以递归下去查询左子树。如果 x x x 大于根结点 r o o t root root 的权值,那么 r o o t root root 的左子树里面的所有权值都比 x x x 要小,所以递归下去查询右子树,并且把左子树的大小加人到答案里面。如果 x x x 等于根结点 r o o t root root 的权值,那么我们已经找到了 x x x ,返回答案即可。
  2. 查询排名为 x x x 的数:
    当査询 r o o t root root 子树中第 x x x 小的值时:
    1. 如果 x x x 小于或等于 r o o t root root 左子树的大小,则排名为 x x x 的数一定在 r o o t root root 的左子树中,递归下去査询 r o o t root root 的左子树中排名为 x x x 的数;
    2. 如果x等于 root 左子树的大小 +1,由于左子树里面的权值都小于根结点 r o o t root root 的权值,右子树里面的权值都大于根结点 r o o t root root 的权值,所以排名为 x x x 的数一定是根结点 r o o t root root 的权值,将其返回即可;
    3. 如果 x x x 大于 “ r o o t root root 左子树的大小+1”,则排名为 x x x 的数一定在 r o o t root root 的右子树中,递归下去查询 r o o t root root 的右子树中排名为“ x − 左子树大小 − 1 的数 x-左子树大小 -1的数 x左子树大小1的数”;
  3. 求x的前驱后继:
    不用专门写两个函数来查询前驱后继,可以先查询 x x x 的排名 r a n k rank rank ,然后查询排名为 r a n k − 1 rank-1 rank1 的数与排名为 r a n k + 1 rank+1 rank+1 的数,这两个查询结果即分别为 x x x 的前驱和后继。
  4. 插入一个数 x x x
    每次将 x x x 和根结点 r o o t root root 的权值比较。如果 x x x小于根结点 r o o t root root 的权值,那么把 x x x插人 r o o t root root 的左儿子里面;如果 x x x 大于根结点 r o o t root root 的权值,那么把 x x x 插入 r o o t root root 的右儿子里面,这样操作之后这个二叉搜索树仍然保持了其性质。如果此时将 x x x 插入的那个位置的结点并不存在,比如要将 x x x 插人 r o o t root root 的左子树中,但是 r o o t root root 的左儿子是空的,则新建一个结点,权值为 x x x,来代替那个不存在的结点,然后回溯的时候更新该结点的 s i z e size size 值。

code:

#include<bits/stdc++.h>
using namespace std;
int n;
struct node{
    int val, num, size;
    int left, right;
    node(int v, int s, int nn, int l, int r):val(v), num(nn), size(s), left(l), right(r){};
    node(){}
};
node t[2000];

void flatten(int root){
	//更新节点信息
    t[root].size = t[t[root].left].size + t[t[root].right].size + t[root].num;
}

void build(int root, int loc){
	//插入节点 / 建树
    if(t[loc].val < t[root].val){
        if(t[root].left == -1){t[root].left = loc;}
        else build(t[root].left, loc);
    }
    else if(t[loc].val > t[root].val){
        if(t[root].right == -1)t[root].right = loc;
        else build(t[root].right, loc);
    }
    else t[root].num++;
    flatten(root);
}
int rank(int x, int root){
//查找x的排名
	if(root){
		if(x < t[root].val){
			return rank(x, t[root].left);
		}
		if(x > t[root].val){
			return rank(x, t[root].right) + t[t[root].left].size + t[root].num;
		}
		return t[t[root].left].size+t[root].num;
	}
	return 1;
}

int find_kth(int root, int k){
//查找排名x的值
    if(k <= t[t[root].left].size){
        return find_kth(t[root].left, k);
    }
    else if(k <= t[root].num + t[t[root].left].size){
        return t[root].val;
    }
    else return find_kth(t[root].right, k - t[root].num - t[t[root].left].size);
}

bool find_value(int root, int v){
//查找是否存在值x
    if(v == t[root].val)return true;
    else if(v < t[root].val && t[root].left != -1) return find_value(t[root].left, v);
    else if(v > t[root].val && t[root].right != -1) return find_value(t[root].right, v);
    return false;
}

int main(){
    int root = 1; cin >> n;
    for(int i = 1; i <= n; i++){
        t[i] = node(0, 1, 1, -1, -1);
        cin >> t[i].val;
        if(i != 1)build(root, i);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fanxinfx2

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值