学习记录 线段树(一)

处理RMQ问题的一个实用的数据结构是线段树,它是一种在每个节点上维护区间信息的二叉搜索树,并且由于一般不删除结点,平衡性较好,因而能实现高效查找、修改的目的。

线段树的根节点维护整个区间的信息,每个结点的左右儿子分别维护父节点左半部分右半部分的区间信息。

下面以单点增加值区间查询最大值为例说明如何利用线段树完成RMQ操作:
为了方便起见,修改与查询均传入每个结点对应的区间
1.建立:为保险起见,将线段树大小设为四倍的区间大小

int seg_tree[M*4],num_nodes;
//...
//计算有效结点个数
for(int i=0; (1<<i)<n; i++);
num_nodes=1<<(i+1)-1;
int t=(1<<n)-1;

2.查询:从根节点开始:

  1. 若结点对应的区间与待查区间完全无交集,返回-INF;
  2. 若结点对应的区间完全包含于待查区间,返回该节点的值;
  3. 其他情况,递归地查找左右子树,并返回其中的较小者。
int query(int st,int end,int k=0,int l=0,int r=t) {
	if(l>end||r<st)    return -INF;
	if(l>=st&&r<=end)	return seg_tree[k];
	int L=query(st,end,k*2+1,l,(l+r)/2);
	int R=query(st,end,k*2+2,1+(l+r)/2,r);
	return max(L,R);
}

复杂度 O ( l o g n ) O(logn) O(logn)

3.更新:按照查询思路更新

void update(int p,int val,int k=0,int l=0,int r=t) {
	if(p>r||p<l)	return;
	if(p>=l&&p<=r) {
	    seg_tree[k]+=val;
	    return;
	}
	update(p,val,k*2+1,l,(l+r)/2);
	update(p,val,k*2+2,1+(l+r)/2);
	seg_tree[k]=max(seg_tree[k*2+1],seg_tree[k*2+2]);
}

复杂度 O ( l o g n ) O(logn) O(logn)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是 Go 语言实现线段树的示例代码: ```go package main import "fmt" // 线段树结构体 type SegmentTree struct { left, right int // 当前节点表示的区间左右端点 sum int // 当前节点表示的区间内的和 leftChild *SegmentTree // 左子节点 rightChild *SegmentTree // 右子节点 } // 创建线段树 func build(nums []int, left, right int) *SegmentTree { if left == right { return &SegmentTree{left: left, right: right, sum: nums[left]} } mid := (left + right) / 2 leftChild := build(nums, left, mid) rightChild := build(nums, mid+1, right) return &SegmentTree{left: left, right: right, sum: leftChild.sum + rightChild.sum, leftChild: leftChild, rightChild: rightChild} } // 区间查询 func query(root *SegmentTree, left, right int) int { if root.left == left && root.right == right { return root.sum } mid := (root.left + root.right) / 2 if right <= mid { return query(root.leftChild, left, right) } else if left > mid { return query(root.rightChild, left, right) } else { return query(root.leftChild, left, mid) + query(root.rightChild, mid+1, right) } } // 区间修改 func modify(root *SegmentTree, index, val int) { if root.left == root.right { root.sum = val return } mid := (root.left + root.right) / 2 if index <= mid { modify(root.leftChild, index, val) } else { modify(root.rightChild, index, val) } root.sum = root.leftChild.sum + root.rightChild.sum } func main() { nums := []int{1, 3, 5, 7, 9, 11} root := build(nums, 0, len(nums)-1) fmt.Println(query(root, 0, 2)) // 输出 9 modify(root, 1, 2) fmt.Println(query(root, 0, 2)) // 输出 8 } ``` 这段代码实现了线段树的基本功能,包括创建线段树、区间查询和区间修改。其中 `SegmentTree` 结构体表示线段树节点,包括左右端点、当前区间内的和以及左右子节点。`build` 函数用于创建线段树,`query` 函数用于区间查询,`modify` 函数用于区间修改。在 `main` 函数中,创建了一个数组并构建了对应的线段树,然后进行了区间查询和修改操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值