数据结构之区间树

区间树是一种数据结构,用于高效地处理区间查询和更新。它将区间划分为单个单元,每个单元对应一个叶节点,非叶节点表示子区间。区间树支持建立、查询、节点更新和范围修改等操作。

1.区间树定义

  • 区间树:区间树是一个区间划分成一些单元区间(即单个数据),每个单元区间对应一个叶节点,非叶节点表示其所代表的子树对应的子区间。区间树中,每个节点的子节点分别表示它的左右半区间。对于每一个非叶节点 ,设其对应区间为[a, b],它的左子树表示的区间为[a, (a+b)/2], 右子树表示的区间为[(a+b)/2+1, b]
    类的各种成员:
struct Node{
	int l, r;
	int val;
	Node()  :l(-1), r(-1), val(-1){}
};

2.区间树操作

2.1 区间树的建立

//既包括begin,也包括end
void build(int node, int l, int r) {
//构造当前的数组的各种成员函数
	itv[node].l = l;
	itv[node].r = r;
	if (l == r) {
		itv[node].val = arr[l];
			return ;
	}
	//继续构建左子树
	build(2 * node, l, l + (r - l) / 2);
	//继续构建右子树
	build(2 * node + 1, l + (r - l) / 2 + 1, r);
	//构建区间树的值,根据题目的需要可以是最大值,最小值,或者求和值
	itv[node].val = itv[2 * node].val + itv[2 * node + 1].val;
}		

2.2 区间树的查询

int query(int node, int l, int r) {
	if (itv[node].l > r || itv[node].r < l) {
		return -1;
	}
	if (l <= itv[node].l && itv[node].r <= r) {
	//假设所需要查询的区间包含了这个当前区间的左右值的时候,就直接返回结果
		return itv[node].val;
	}
	int resl = query(2 * node, l ,r);
	int resr = query(2 * node + 1, l ,r);
	//左子树查询不成功,返回右子树的值		
	if (resl == -1) return resr;		
	//右子树查询不成功,返回左子树的值
	if (res r== -1) return resl;
	//左右子树都成功的时候,返回符合条件的值
	return resl + resr;
}		

2.3 区间树的某个节点进行更新

//代码的思想有点类似于构建区间树,拥有二分查找的思想
void update(int node,i nt key, int value) {
	if (itv[node].l == itv[node].r) {
		itv[node].val = value;
	} else {
		int mid = itv[node].l + (itv[node].r - itv[node].l) / 2;
		//这里注意等号,等号的话递归左子树
		if (key <= mid) {
			//mid在构建线段树的时候是包括在左子树的,因此等号应该递归左子树
			update(2 * node, key, value);
		} else {
			update(2 * node + 1, key, value);
		}
		itv[node].val = itv[2 * node].val + itv[2 * node + 1].val;
	}
} 

2.4 区间树的对一定范围的区间的数值进行修改

//进行区间数值查询的时候也要进行down操作
/*if (itv[node].k > 0) {
	down(node);
}*/
void down(int node) {
	itv[2 * node].k += itv[node].k;
	itv[2 * node+1].k += itv[node].k;
	itv[2 * node].val += itv[2*node].k * (itv[2 * node].r-itv[2 * node].l + 1);
	itv[2  *node+1].val += itv[2 * node + 1].k * (itv[2 * node + 1].r-itv[2 * node + 1].l + 1);
	itv[node].k = 0;
}
void add(int node, int l, int r, int value){
	//如果当前区间满足条件,直接进行lazy的赋值操作
	if (l <= itv[node].l && itv[node].r <= r) {
		itv[node].val += (itv[node].r - itv[node].l + 1) * value;
		itv[node].k += value;
	} else {
		if (itv[node].k > 0) {
		//如果上一次的lazy还没处理,先进行处理
		down(node);
	}
	int mid = itv[node].l + (itv[node].r - itv[node].l) / 2;
	//这里的等号需要另外注意
	if (r <= mid) {
	//mid在构建线段树的时候是包括在左子树的,因此等号应该递归左子树		
		add(2 * node, l, r, value);
	} else if(l > mid) {
	//mid在构建线段树的时候是包括在左子树的,因此等号不应该递归右子树
		add(2  *node + 1, l, r, value);
	} else {
	//其他情况应该同时递归左子树和右子树
		add(2 * node, l, r, value);
		add(2 * node + 1, l, r, value);
	}
	itv[node].val=itv[2*node].val+itv[2*node+1].val;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值