当前位置: 首页 > article >正文

线段树【C语言】【C++】

线段树(Segment Tree)是一种用于处理区间(segment)相关问题的数据结构,它可以高效地进行区间查询区间更新。线段树特别适用于需要频繁查询某种统计信息(如最小值最大值总和等)并且可以快速进行修改(更新区间或单点)的场景。

1. 线段树的基本概念

结构:线段树通常被实现为一棵近似完全二叉树。每个节点表示某个区间的一个属性(如和、最大值等),叶子节点代表数组的元素。

构建:构建线段树的过程通常需要 O(n) 的时间复杂度,其中 n 是数组长度。

查询:对于给定的区间查询,线段树可以在 O(log n) 时间内返回结果。

更新:对某个元素的单点更新或对一个区间的更新也可以在 O(log n) 时间内完成。

2. 线段树的应用

线段树常用于解决以下问题:

  • 查询区间的最大值最小值平均值等。

  • 在给定区间内更新某些元素的值(如替换)。

  • 单点更新(改变一个元素的值)。

  • 支持询问区间中某个条件的统计。

3. 线段树的实现(注意,node数组大小一般为数据量的4倍!)

结构体和宏 

 C语言版本

#define max(a,b) ((a)>(b)?(a):(b))
typedef struct Node {
	int data, lazy;
} node;

 C++版本

struct node {
	int data, lazy = 0;
};

build函数 

构建线段树

构建线段树的基本步骤如下:

  1. 如果当前节点表示的区间是一个单点(即 left == right),将其值设置为数组中的对应值。

  2. 否则:

    • 递归构建左子树和右子树。

    • 根据子树的信息更新当前节点的值。

 C语言版本

void build(int *data, node *tree, int rt, int left, int right) {
	if (left == right) {
		tree[rt].data = data[left];
		tree[rt].lazy = 0;
		return ;
	}
	int mid = (left + right) / 2;
	build(data, tree, 2 * rt, left, mid);
	build(data, tree, 2 * rt + 1, mid + 1, right);
	tree[rt].data = max(tree[2 * rt].data, tree[2 * rt + 1].data);
	tree[rt].lazy = 0;
}

C++版本

void build(const vector<int>&data, vector <node>&tree, int rt, int left, int right) {
	if (left == right) {
		tree[rt].data = data[left];
		return ;
	}
	int mid = left + (right - left) / 2;
	build(data, tree, 2 * rt, left, mid);
	build(data, tree, 2 * rt + 1, mid + 1, right);
	tree[rt].data = max(tree[2 * rt].data, tree[2 * rt + 1].data);
}

pushDown函数

懒惰更新是一种优化,通过在树的节点中额外存储一个懒惰变量(lazy),来延迟更新操作,从而提高效率。懒惰更新的基本思路是,当需要更新一个区间时,不立即更新子节点,而是标记当前节点,等到查询或者后续更新再处理

pushDown函数是对当前节点进行下发操作,将当前节点储存的懒惰变量下发给左右孩子,并归零懒惰变量

C语言版本

void pushDown(node *tree, int rt) {
	if (tree[rt].lazy != 0) {
		tree[2 * rt].data += tree[rt].lazy;
		tree[2 * rt].lazy += tree[rt].lazy;
		tree[2 * rt + 1].data += tree[rt].lazy;
		tree[2 * rt + 1].lazy += tree[rt].lazy;
		tree[rt].lazy = 0;
	}
}

C++版本

void pushDown(vector<node>&tree, int rt) {
	if (tree[rt].lazy != 0) {
		tree[2 * rt].data += tree[rt].lazy;
		tree[2 * rt].lazy += tree[rt].lazy;
		tree[2 * rt + 1].data += tree[rt].lazy;
		tree[2 * rt + 1].lazy += tree[rt].lazy;
		tree[rt].lazy = 0;
	}
}

query函数

查询区间(代码展示的是查询最大值

当我们想要查询某个区间 [ql, qr] 的值 (maxsum 等) 时,通常会执行以下操作:

  1. 如果查询的区间与当前节点的区间不重叠,返回一个很小的初始值(如 -inf)。

  2. 如果查询的区间完全包含当前区间,返回当前节点的值

  3. 否则,递归查询左右子树的结果,并合并结果。

C语言版本

int query(node *tree, int rt, int left, int right, int ql, int qr) {
	if (ql > right || qr < left) {
		return -1e9;
	}
	if (ql <= left && qr >= right) {
		return tree[rt].data;
	}
	int mid = (left + right) / 2;
	pushDown(tree, rt);
	int lmax = query(tree, 2 * rt, left, mid, ql, qr);
	int rmax = query(tree, 2 * rt + 1, mid + 1, right, ql, qr);
	return max(lmax, rmax);
}

C++版本

int query(vector<node>&tree, int rt, int left, int right, int ql, int qr) {
	if (ql > right || qr < left) {
		return INT_MIN;
	}
	if (ql <= left && qr >= right) {
		return tree[rt].data;
	}
	int mid = left + (right - left) / 2;
	pushDown(tree, rt);
	int leftmax = query(tree, 2 * rt, left, mid, ql, qr);
	int rightmax = query(tree, 2 * rt + 1, mid + 1, right, ql, qr);
	return max(leftmax, rightmax);
}

 update函数

更新操作

要更新某个元素(如将数组中索引为 idx 的元素更新为 value),可以:

  1. 如果当前节点表示的区间是 left == right,则直接更新该叶子节点。

  2. 否则,递归更新左右子树并更新当前节点的值。

C语言版本

void update(node *tree, int rt, int left, int right, int idx, int value) {
	if (left == right) {
		tree[rt].data = value;
		tree[rt].lazy = 0;
		return ;
	}
	int mid = (left + right) / 2;
	pushDown(tree, rt);
	if (idx <= mid) {
		update(tree, 2 * rt, left, mid, idx, value);
	} else {
		update(tree, 2 * rt + 1, mid + 1, right, idx, value);
	}
	tree[rt].data = max(tree[2 * rt].data, tree[2 * rt + 1].data);
}

C++版本 

void update(vector<node>&tree, int rt, int left, int right, int idx, int value) {
	if (left == right) {
		tree[rt].data = value;
		tree[rt].lazy = 0;
		return ;
	}
	int mid = left + (right - left) / 2;
	pushDown(tree, rt);
	if (idx <= mid) {
		update(tree, 2 * rt, left, mid, idx, value);
	} else {
		update(tree, 2 * rt + 1, mid + 1, right, idx, value);
	}
	tree[rt].data = max(tree[2 * rt].data, tree[2 * rt + 1].data);
}

updateRange函数 

区间更新:(代码展示的是查询最大值

updateRange 函数用于在区间 [ul, ur] 上加上一个值 value,并使用 lazy 更新来延迟更新子节点。

C语言版本

void updateRange(node *tree, int rt, int left, int right, int ul, int ur, int value) {
	if (ul > right || ur < left)return ;
	if (ul <= left && ur >= right) {
		tree[rt].data += value;
		tree[rt].lazy += value;
		return ;
	}
	int mid = (left + right) / 2;
	pushDown(tree, rt);
	updateRange(tree, 2 * rt, left, mid, ul, ur, value);
	updateRange(tree, 2 * rt + 1, mid + 1, right, ul, ur, value);
	tree[rt].data = max(tree[2 * rt].data, tree[2 * rt + 1].data);
}

 C++版本

void updateRange(vector<node>&tree, int rt, int left, int right, int ul, int ur, int value) {
	if (ul > right || ur < left) {
		return ;
	}
	if (ul <= left && ur >= right) {
		tree[rt].data += value;
		tree[rt].lazy += value;
		return ;
	}
	int mid = left + (right - left) / 2;
	pushDown(tree, rt);
	updateRange(tree, 2 * rt, left, mid, ul, ur, value);
	updateRange(tree, 2 * rt + 1, mid + 1, right, ul, ur, value);
	tree[rt].data = max(tree[2 * rt].data, tree[2 * rt + 1].data);
}

4.总结

线段树是一种强大的数据结构,能够有效处理区间相关的多个问题,且时间复杂度较低适合动态数据的频繁查询与更新。虽然实现相对复杂,但掌握这一结构能够为解决多种算法问题提供支持。在实际应用中,线段树非常有用,是竞赛编程或高效算法设计的重点之一。

5.学习视频(推荐的)

线段树详解https://www.bilibili.com/video/BV1vhPoeXEFf/?spm_id_from=333.1387.homepage.video_card.click&vd_source=ddbcf617257651579074a19fa12d5236


http://www.kler.cn/a/556403.html

相关文章:

  • pycharm 调试 debug 进入 remote_sources
  • 【Vue3 项目中父子组件之间如何互相传值、传递方法】
  • uni-app(位置1)
  • 深蕾科技智能多媒体SoC产品助力“DataEye剧查查之夜”微短剧盛会
  • Spring Boot (maven)分页4.0.1版本 专业版- 改
  • 如何连接别人的redis服务器吗?
  • 同ip访问不同网页的效果
  • 【推荐项目】009-学校宿舍管理系统
  • 【PyTorch 深度学习常用 Linux 指令总结】
  • ubuntu docker 安装 deepseek anythingllm/openwebui教程
  • npm包无法识别命令
  • 亚马逊AI图像模型Nova深度体验(含源代码)(上)
  • 深度测评 | AI引领安全运营提升用户使用感受及分析处置能力
  • Docker小雅Emby全家桶配置夸克网盘Cookie教程
  • Django Admin: 实现基于数据库实际值的动态过滤器
  • 微信小程序消息推送解密
  • c++:stack与deque
  • 英文字体:极简现代浓缩未来派科技海报标题排版无衬线字体 PODIUM Sharp Font
  • SRS编译arm版本出错 ERROR: opus not found using pkg-config
  • 体育电竞比分网开发流程