突然想学习一下线段树了,看了一下基本的知识,尝试了一道简单的线段树题目。
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100010;
struct node {
int sum;
int l;
int r;
};
struct node tree[4 * N]; //树数组长度需要区间长度的 4 倍
int a[N]; //level即区间
void build(int t, int l, int r) { //建树,这里用数组存放树,类似的情况出现在堆排序中,节点 i 的左节点为 2*i,右节点 2*i + 1
tree[t].l = l;
tree[t].r = r; //根节点区间为 0-n,左子节点区间 0- n/2, 右子节点区间 n/2 - n, 左闭右开;
if (l == r - 1) { // 这里的区间 2-3(已经到叶子节点),即代表level 3, sum的数值表示此区间元素的数量
tree[t].sum = a[l];
return;
}
int mid = (l + r) / 2; //递归建左子树,递归建右子树,根sum的值为左右子树sun的和
build(2 * t, l, mid);
build(2 * t + 1, mid, r);
tree[t].sum = tree[2 * t].sum + tree[2 * t + 1].sum;
}
void update(int t, int l, int r, int value) { //更新操作,从节点t开始更新, 将区间l-r的元素数量变为 value
if (tree[t].l >= l && tree[t].r <= r) { //即改变level r中的元素个数
tree[t].sum = value;
return;
}
if (tree[t].l == tree[t].r - 1)
return;
int mid = (tree[t].l + tree[t].r) / 2;
if (l <= mid) //如果根节点区间的中值 >= 区间l-r的左端
update(2 * t, l, r, value); //那么在左子树中更新
if (r > mid) //如果根节点区间的中值 < 区间l-r的右端
update(2 * t + 1, l, r, value); //那么右子树也需要更新
tree[t].sum = tree[2 * t].sum + tree[2 * t + 1].sum; //左右子树更新之后,更新根节点的sum值
}
int query(int t, int len) { //给定一个排名 len, 查询其处于哪一个level
if (tree[t].l == tree[t].r - 1) { //查找到区间, 返回level
return tree[t].r;
}
if (len <= tree[2 * t].sum) //如果排名 小于左子树元素数量,区间肯定在左子树中
return query(2 * t, len);
else //否则,在右子树查询排名为 (len - 左子树元素数量)的区间
return query(2 * t + 1, len - tree[2 * t].sum);
}
int main()
{
int n;
while (cin >> n) {
for (int i = 0; i < n; ++i)
cin >> a[i];
memset(tree, 0, sizeof(tree));
build(1, 0, n);
int k;
cin >> k;
while (k--) {
char type;
cin >> type;
if (type == 'p') {
int level, num;
cin >> level >> num;
update(1, level - 1, level, num);
}
else {
int len;
cin >> len;
cout<<query(1, len)<<endl;
}
}
}
return 0;
}