SegmentTree 的基本操作

本文介绍了一种通用的线段树实现模板,并通过实例演示了如何构建和使用该模板进行区间查询及更新操作。文章详细解释了线段树的基本构造方法、查询算法和更新机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


#pragma once
#include <iostream>

#define inf 0x3f3f3f3f
template<typename T>
class SegmentTree {

private:
	T* data;
	T* tree;
	T* add;
	int number;

	int getLeftIndex(int treeIndex) {
		return treeIndex * 2 + 1;
	}
	int getRightIndex(int treeIndex) {
		return treeIndex * 2 + 2;
	}
	//以treeIndex为根节点,【l,r】创建线段树
	void bulidSegmentTree(int treeIndex, int l, int r) {
		if (l == r) {
			tree[treeIndex] = data[l];
			return;
		}
		// 不使用(l+r)/2因为可能会超出整数范围
		int mid = l + (r - l) / 2;
		int leftIndex = getLeftIndex(treeIndex);
		int RightIndex = getRightIndex(treeIndex);
		//分治
		bulidSegmentTree(leftIndex, l, mid);
		bulidSegmentTree(RightIndex, mid + 1, r);

	//	tree[treeIndex] = data[l] + data[r];
		//融合
		tree[treeIndex] = merge(tree[leftIndex],tree[RightIndex]);
	}
	int query1(int rootNode, int l, int r, int queryL, int queryR) {
		
		if (l == queryL && queryR == r)
			return tree[rootNode];
		int mid = l + (r - l) / 2;
		if (queryR <= mid)
			return query1(rootNode * 2 + 1, l, mid, queryL, queryR);
		else if (queryL >= mid + 1)
			return query1(rootNode * 2 + 2, mid + 1, r, queryL, queryR);
		else
			return merge(query1(rootNode * 2 + 1, l, mid, queryL, mid), 
				query1(rootNode * 2 + 2, mid + 1, r, mid+1, queryR));
		
	}
	//在【l,r】范围内, 将p的值修改为v
	void updataPoint(int rootNode, int l, int r, int p, int v) {
		if (l == r) {
			tree[rootNode] = v;
			return;
		}
		int mid = l + (r - l) / 2;
		if (p <= mid)
			updataPoint(rootNode * 2 + 1, l, mid, p, v);
		else if( p >= mid+1)
			updataPoint(rootNode * 2 + 2, mid + 1, r, p, v);
		tree[rootNode] = merge(tree[rootNode*2+1],tree[rootNode*2+2]);
	}

    void updata(int rootNode, int l, int r, int ql, int qr, int v) {
		if (ql <= l && qr >= r) {
			add[rootNode] += v;
			tree[rootNode] += v * (r - l + 1);
			return;
		}

		pushDwon(rootNode,l,r);
		int mid = l + (r - l) / 2;
		int left = rootNode * 2 + 1;
		int right = rootNode * 2 + 2;

		if (ql <= mid)
			updata(left, l, mid, ql, qr, v);
		if (qr >= mid + 1)
			updata(right, mid + 1, r, ql, qr, v);
		tree[rootNode] = merge(tree[left],tree[right]);
	}

    void pushDwon(int rootNode,int l, int r){
		
		if (add[rootNode]) {
			int left = rootNode * 2 + 1;
			int right = rootNode * 2 + 2;
			add[left] += add[rootNode];
			add[right] += add[rootNode];

			
			int mid = l + (r - l) / 2;

	//		tree[left] += v * (mid-l+1);		//[l,mid]
	//		tree[right] += v * (r - mid);		//[mid+1,r]
			tree[left] = add[rootNode] * (mid - l + 1);
			tree[right] = add[rootNode] * (r - mid);
			add[rootNode] = 0;
		}
	}


 public:
     SegmentTree(){ }
     SegmentTree(T arr[],int n){
 	
	this->number = n;
	data = new T[n];
	add = new T[n * 4]{ 0 };
	for (int i = 0; i < n; i++) {
		data[i] = arr[i];
	}
	tree = new T[n * 4];
	for (int i = 0; i < n * 4; i++) {
		tree[i] = inf;
	}
	bulidSegmentTree(0,0,n-1);
}
	T merge(T a, T b) {return a + b;}
	int getTreelength() {return 4 * number;}
	void println() {
		cout << "[ ";
		for (int i = 0; i < getTreelength(); i++) {
			if (tree[i] != inf)
				cout << tree[i];
			else
				cout << "NUll";
			if (i < getTreelength() - 1)
				cout << ",";
		}
		cout << " ]" << endl;
	}
	//区间查询
	int query1(int queryL, int queryR) {
		return query1(0, 0, number - 1, queryL, queryR);
	}

    //点更新 P位置的值更新为v
	void updataPoint(int p, int v) {
		updataPoint(0, 0, number-1, p, v);
	}

    //区间更新
	void updata(int l, int r, int v) {
		updata(0, 0, number - 1, l, r, v);
	}
};



#include <iostream>
#include "SegmentTree.h"
using namespace std;

int main()
{
    int arr[] = { 1,2,3,4,5,6 };
    SegmentTree<int> segment(arr, 6);
    segment.println();
    cout << segment.query1(0, 5) << endl;
    //segment.updataPoint(1,3);
    //cout << segment.query1(0, 5) << endl;
    cout << segment.query1(0, 2) << endl;
    segment.updata(0, 2, 2);
    cout << segment.query1(0, 2) << endl;
    system("pause");
    
    return 0;
}




<think>好的,我现在需要帮助用户理解如何在线段树中实现区间查询操作。用户已经提到了参考内容中的几个引用,特别是引用[2],其中涉及线段树的基本概念和操作。首先,我应该回顾线段树的基本结构,然后详细说明区间查询的步骤。 首先,线段树是一种二叉树结构,每个节点代表一个区间。叶子节点对应单个元素,而非叶子节点则代表其子节点的合并区间。区间查询的关键在于如何有效地分解查询区间,并合并子节点的结果。 接下来,我需要考虑如何递归地处理查询。比如,当查询区间完全覆盖当前节点的区间时,直接返回该节点的值。如果查询区间部分覆盖当前节点的区间,则需要递归查询左右子节点,并将结果合并。同时,要处理延迟更新(如果有的话),但用户的问题可能暂时不需要涉及惰性传播,不过引用[1]提到了惰性段树,可能需要简要提及。 然后,我需要确保解释清楚递归的终止条件和合并结果的步骤。例如,在求和操作中,合并就是相加;在最小值操作中,合并就是取较小值。这部分需要具体说明,可能通过示例来展示。 用户提供的示例代码是关于快速排序的,但用户的问题是关于线段树的区间查询,所以需要给出线段树的相关代码示例。根据引用[2]中的内容,可能参考其中的实现方法。不过用户提供的引用[1]中的Python代码可能更适合作为例子,因为它是关于惰性段树的,但用户的问题可能不需要惰性部分,所以需要调整。 另外,需要注意用户提到的格式要求,尤其是数学表达式使用$...$,独立公式用$$...$$,并且生成相关问题。同时,引用需要正确标注,比如引用[^2]和[^3]。 可能的步骤: 1. 解释线段树的结构和区间查询的基本思想。 2. 分步骤说明递归查询的过程。 3. 给出具体的代码示例,比如求和或最小值的查询。 4. 处理可能的边界情况。 5. 提及惰性传播的扩展(如果有必要)。 6. 生成相关问题。 需要确保代码示例正确,并且符合Python语法,同时注释解释关键步骤。可能还需要对比不同的操作类型(如求和、最小值)在合并时的差异。 现在需要检查是否有遗漏的要点,比如区间分解的条件,递归的终止条件,以及合并结果的函数。同时,引用需要正确对应到参考内容中的相关部分,比如引用[2]讨论了线段树的基本操作,而引用[1]提到惰性传播,可能在优化部分提及。 最后,生成的相关问题需要与线段树的区间查询相关,比如优化方法、应用场景或与其他数据结构的比较。</think>### 线段树的区间查询操作实现步骤 线段树(Segment Tree)是一种用于高效处理**区间查询**(如区间求和、最小值、最大值等)和**区间更新**的数据结构。其核心思想是将区间递归分解为子区间,通过二叉树结构存储预处理结果,使得查询时间复杂度为$O(\log n)$。 #### 1. 线段树的结构 线段树是二叉树,每个节点表示一个区间$[l, r]$: - **叶子节点**:区间长度为1(如$[3,3]$),直接存储对应元素值。 - **非叶子节点**:存储子区间合并后的结果(如区间和、最小值等)。 - **合并函数**:根据需求定义(如求和用加法,最小值用$\min$)。 #### 2. 区间查询步骤 假设要查询区间$[q_l, q_r]$的值,递归过程如下: 1. **完全覆盖**:若当前节点区间$[l, r]$完全包含在$[q_l, q_r]$中,直接返回节点值。 2. **部分重叠**:否则,将查询分解到左右子节点: - 计算中点$mid = \lfloor (l + r) / 2 \rfloor$ - 若$q_l \leq mid$,递归查询左子树。 - 若$q_r > mid$,递归查询右子树。 3. **合并结果**:将左右子树查询结果按合并函数计算最终值。 #### 3. 代码示例(区间求和查询) ```python class SegmentTree: def __init__(self, data): self.n = len(data) self.tree = [0] * (4 * self.n) # 初始化树结构 self.data = data self.build(0, 0, self.n - 1) def build(self, node, l, r): if l == r: self.tree[node] = self.data[l] else: mid = (l + r) // 2 left = 2 * node + 1 right = 2 * node + 2 self.build(left, l, mid) self.build(right, mid+1, r) self.tree[node] = self.tree[left] + self.tree[right] # 合并为区间和 def query_range(self, q_l, q_r): return self._query(0, 0, self.n - 1, q_l, q_r) def _query(self, node, l, r, q_l, q_r): if r < q_l or l > q_r: return 0 # 区间无交集,返回不影响合并的值(如求和时为0) if q_l <= l and r <= q_r: return self.tree[node] # 完全覆盖 mid = (l + r) // 2 left_sum = self._query(2*node+1, l, mid, q_l, q_r) right_sum = self._query(2*node+2, mid+1, r, q_l, q_r) return left_sum + right_sum # 合并左右子树结果 # 示例用法 data = [1, 3, 5, 7, 9] st = SegmentTree(data) print(st.query_range(1, 3)) # 输出3+5+7=15 ``` #### 4. 关键点说明 - **时间复杂度**:每次查询最多遍历树高$O(\log n)$。 - **合并函数**:可根据需求修改,如求最小值时替换为`min(left_min, right_min)`。 - **惰性传播(可选)**:若支持区间更新,需引入延迟标记(Lazy Propagation)优化性能[^1]。 #### 5. 应用场景 - 动态区间统计:实时游戏积分榜排名、股票区间波动分析。 - 范围过滤:地图应用中搜索某个矩形区域内的POI点。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值