目录
一.线段树是什么?(线段树的定义)
线段树(Segment Tree)是一种常见的数据结构,用于处理区间查询和更新问题。它将给定的区间划分为若干个子区间,每个子区间对应着线段树中的一个节点。通过构建线段树,我们可以在对数时间内处理区间查询和更新操作,这极大地提高了问题的解决效率。
二.线段树的作用(线段树适合些什么题型)
1.最小值、最大值、总和
2.区间更新的最小值、最大值、总和等等
三.线段树的理解
线段树通常是一棵平衡二叉树,每个节点都包含一个区间范围以及对应的值。通过预处理的方式,我们可以在构建线段树时,将原始数组中的元素进行合并,从而实现区间查询和更新的高效操作。
线段树的功能介绍
1.线段树的定义
如果我们已经知道有n个节点了,node数组大小一般为数据量的4倍
如果它是一颗完全二叉树,node数组大小可以为数据量的2倍
如果它不是是一颗完全二叉树,node数组大小需要为数据量的4倍(像下图这样)
c语言 实现
//定义线段树的节点信息(值和懒标记)
int max(int a, int b) {
if (a > b)return a;
return b;
}
typedef struct node {
int date, lazy;
};
node[4*n];
c++ 实现
// 定义线段树的节点信息(值和懒标记)
struct Node {
int date, lazy;
};
2. 线段树的创建
c语言 实现
void build(node tree[], int date[], int rt, int left, int right) {
//只有一个孩子即使他自己的最大值,也是叶子节点
if (left == right) {
date[rt] = date[left];
return;
}
int mid = left + (right - left) / 2;
//递归左子树
build(tree, date, 2 * rt, left, mid);
//递归右子树
build(tree, date, 2 * rt + 1, mid + 1, right);
tree[rt].date = max(tree[2 * rt].date, tree[2 * rt + 1].date);
}
c++ 实现
// tree表示线段树,date表示输入的数据,rt表示当前节点,
// left right表示当前节点的左右边界
void build(vector<Node>& tree, const vector<int>& date, int rt, int left, int right) {
if (left == right) {
tree[rt].date = date[left];
tree[rt].lazy = 0;
return;
}
int mid = left + (right - left) / 2;
build(tree, date, 2 * rt, left, mid);
build(tree, date, 2 * rt + 1, mid + 1, right);
tree[rt].date = max(tree[2 * rt].date, tree[2 * rt + 1].date);
tree[rt].lazy = 0;
}
3.线段树的懒标记
c语言 实现
void pushDown(node tree[], int rt) {
if (tree[rt].lazy != 0) {
//更新左子树
tree[rt * 2].date += tree[rt].lazy;
tree[rt * 2].lazy += tree[rt].lazy;
//更新右子树
tree[rt * 2 + 1].date += tree[rt].lazy;
tree[rt * 2 + 1].lazy += tree[rt].lazy;
//赋值懒标记为0
tree[rt].lazy = 0;
}
}
c++ 实现
// 懒标记定义
void pushDown(vector<Node>& tree, int rt) {
if (tree[rt].lazy != 0) {
tree[rt * 2].date += tree[rt].lazy;
tree[rt * 2].lazy += tree[rt].lazy;
tree[rt * 2 + 1].date += tree[rt].lazy;
tree[rt * 2 + 1].lazy += tree[rt].lazy;
tree[rt].lazy = 0;
}
}
4.线段树的查询
c语言 实现
//查询节点最大最小值
int query(node tree[], int rt, int left, int right, int ql, int qr) {
if (ql > right || qr < left) {
return 0;
}
else if (ql <= left && qr >= right) {
return tree[rt].date;
}
pushDown(tree, rt);//处理懒标记
int mid = left + (right - left) / 2;
int leftNum = (tree, 2 * rt, left, mid, ql, qr);//左子树
int rightNum = (tree, 2 * rt + 1, mid + 1, right, ql, qr);//右子树
return max(leftNum, rightNum);
}
c++ 实现
// 查询节点最大值
int query(const vector<Node>& tree, int rt, int left, int right, int ql, int qr) {
if (ql > right || qr < left) {
return 0;
} else if (ql <= left && qr >= right) {
return tree[rt].date;
}
pushDown(tree, rt);
int mid = left + (right - left) / 2;
int leftNum = query(tree, 2 * rt, left, mid, ql, qr);
int rightNum = query(tree, 2 * rt + 1, mid + 1, right, ql, qr);
return max(leftNum, rightNum);
}
5.线段树的更新节点值
c语言 实现
//更新节点值
void update(node tree[], int rt, int left, int right, int index, int value) {
if (left == right) {
tree[rt].date = value;
tree[rt].lazy = 0;
return;
}
int mid = left + (right - left) / 2;
pushDown(tree, rt);//处理懒标记
if (mid >= index){
update(tree, rt, left, mid, index, value);
}
else{
update(tree, rt, mid + 1, right, index, value);
}
tree[rt].date = max(tree[rt * 2].date, tree[rt * 2 + 1].date);
}
c++ 实现
// 更新节点值
void update(vector<Node>& tree, int rt, int left, int right, int index, int value) {
if (left == right) {
tree[rt].date = value;
tree[rt].lazy = 0;
return;
}
int mid = left + (right - left) / 2;
pushDown(tree, rt);
if (mid >= index) {
update(tree, rt, left, mid, index, value);
} else {
update(tree, rt, mid + 1, right, index, value);
}
tree[rt].date = max(tree[rt * 2].date, tree[rt * 2 + 1].date);
}
6.线段树的更新区间节点值
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].date += value;//该节点的最大值加一个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, right, ul, ur, value);
tree[rt].date = max(tree[rt * 2].date, tree[rt * 2 + 1].date);
}
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].date += 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].date = max(tree[rt * 2].date, tree[rt * 2 + 1].date);
}
代码总和
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
//定义线段树的节点信息(值和懒标记)
int max(int a, int b) {
if (a > b)return a;
return b;
}
typedef struct node {
int date, lazy;
};
//tree表示线段树,date表示输入的数据,rt表示当前节点,
// left right表示当前节点的左右边界
//这里的左右边界都是数组的索引
void build(node tree[], int date[], int rt, int left, int right) {
//只有一个孩子即使他自己的最大值,也是叶子节点
if (left == right) {
date[rt] = date[left];
return;
}
int mid = left + (right - left) / 2;
//递归左子树
build(tree, date, 2 * rt, left, mid);
//递归右子树
build(tree, date, 2 * rt + 1, mid + 1, right);
tree[rt].date = max(tree[2 * rt].date, tree[2 * rt + 1].date);
}
//懒标记定义
void pushDown(node tree[], int rt) {
if (tree[rt].lazy != 0) {
//更新左子树
tree[rt * 2].date += tree[rt].lazy;
tree[rt * 2].lazy += tree[rt].lazy;
//更新右子树
tree[rt * 2 + 1].date += tree[rt].lazy;
tree[rt * 2 + 1].lazy += tree[rt].lazy;
//赋值懒标记为0
tree[rt].lazy = 0;
}
}
//查询节点最大最小值
int query(node tree[], int rt, int left, int right, int ql, int qr) {
if (ql > right || qr < left) {
return 0;
}
else if (ql <= left && qr >= right) {
return tree[rt].date;
}
pushDown(tree, rt);//处理懒标记
int mid = left + (right - left) / 2;
int leftNum = (tree, 2 * rt, left, mid, ql, qr);//左子树
int rightNum = (tree, 2 * rt + 1, mid + 1, right, ql, qr);//右子树
return max(leftNum, rightNum);
}
//更新节点值
void update(node tree[], int rt, int left, int right, int index, int value) {
if (left == right) {
tree[rt].date = value;
tree[rt].lazy = 0;
return;
}
int mid = left + (right - left) / 2;
pushDown(tree, rt);//处理懒标记
if (mid >= index){
update(tree, rt, left, mid, index, value);
}
else{
update(tree, rt, mid + 1, right, index, value);
}
tree[rt].date = max(tree[rt * 2].date, tree[rt * 2 + 1].date);
}
//更新一个区间的节点值
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].date += value;//该节点的最大值加一个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, right, ul, ur, value);
tree[rt].date = max(tree[rt * 2].date, tree[rt * 2 + 1].date);
}
四.总结
总的来说,线段树是一种高效的数据结构,适用于解决需要频繁进行区间操作的问题。通过合理构建线段树,可以在较短的时间内完成区间查询和更新操作,提高算法的效率。