线段树知识点理解及其模板代码
2017-08-25 15:38
423 查看
看完树状数组后, 终于发现有些地方还是需要用线段树来解题,还是 开始了解线段树了;
线段树, 真正的像一棵树了, 不想树状数组,似数不是树,较难理解, 线段树是一个平衡树, 他和树状数组有一样的功能, 包括构造线段树,区间查询,单节点更新,以及区间更新;
从比人那扒了一张图:
线段树, 有根节点,和 左右子树, 叶子节点为区间左右值, 而根节点来存 区间里要存的值, 例如:存最小值, 则 区间[0,1] 最小值是2; 2 为当前根节点;
绿色的是根节点,也就是要存的值, 白色的为叶子,也就是我们输进去的区间左右值;
线段树的精髓就是这样, 把要存的值一次一次的 存到根节点;
区间[a,b] 中间就是 (a+b)/2;
构造线段树 可以是普通数组,也可以是 结构体l,这里是结构体;
构造结果为:
构造完后; 要进行查询; 查询的 思路是 从根节点开始递归查询,先左后右,一次进行, 如果查询值与区间无交集返回INF,有交集返回值 最后取最小值;
例如 查询 (3,5) 之间最小值, 应该是 3;
单节点更新, 因为 修改某一节点 会影响他的父亲节点, 因此,需要递归依次修改;
例如 我们在 (3,5) 之间 把 3 的值 -2; 从 4变成2; 则 (3,5) 最小值从3 变成2
区间修改:
区间修改 用到一个延迟标记的东西, 很神奇,
做法: 需要在构造数时 添加 一个标记;
区间修改时 添加pash 延时函数;
代码:
struct SegTree{
int val;
int addmark;
}Seg[maxn];
int array[maxn];
void build(int root,int begin,int end)
{
Seg[root].addmark=0;//为区间修改做准备¸
if(begin==end)
Seg[root].val=array[begin];
else
{
int mid=(begin+end)>>1;
build(root*2+1,begin,mid);//左子树
build(root*2+2,mid+1,end);//右子树
Seg[root].val=min(Seg[root*2+1].val,Seg[root*2+2].val);
}
}
void push(int root)
{
if(Seg[root].addmark!=0)
{
Seg[root*2+1].addmark+=Seg[root].addmark;
Seg[root*2+2].addmark+=Seg[root].addmark;
Seg[root*2+1].val+=Seg[root].addmark;
Seg[root*2+2].val+=Seg[root].addmark;
Seg[root].addmark=0;
}
}
例如 我们将(3,5)所有的值都-2 则 有4-2=2,3-2=1; 最小值为1:
线段树, 真正的像一棵树了, 不想树状数组,似数不是树,较难理解, 线段树是一个平衡树, 他和树状数组有一样的功能, 包括构造线段树,区间查询,单节点更新,以及区间更新;
从比人那扒了一张图:
线段树, 有根节点,和 左右子树, 叶子节点为区间左右值, 而根节点来存 区间里要存的值, 例如:存最小值, 则 区间[0,1] 最小值是2; 2 为当前根节点;
绿色的是根节点,也就是要存的值, 白色的为叶子,也就是我们输进去的区间左右值;
线段树的精髓就是这样, 把要存的值一次一次的 存到根节点;
区间[a,b] 中间就是 (a+b)/2;
构造线段树 可以是普通数组,也可以是 结构体l,这里是结构体;
/* 线段树构造 root:节点 begin:开始 end结束; */ struct SegTree{ int val; int addmark; }Seg[maxn]; int array[maxn]; void build(int root,int begin,int end) { if(begin==end) Seg[root].val=array[begin]; else { int mid=(begin+end)>>1; build(root*2+1,begin,mid);//左子树 build(root*2+2,mid+1,end);//右子树 Seg[root].val=min(Seg[root*2+1].val,Seg[root*2+2].val); } }
构造结果为:
构造完后; 要进行查询; 查询的 思路是 从根节点开始递归查询,先左后右,一次进行, 如果查询值与区间无交集返回INF,有交集返回值 最后取最小值;
/* 区间查询 */ int query(int root,int begin,int end,int left 4000 ,int right) { if(right<begin||left>end) return INF; if(left<=begin&&right>=end) return Seg[root].val; int mid=(begin+end)>>1; return min(query(root*2+1,begin,mid,left,right),query(root*2+2,mid+1,end,left,right)); }
例如 查询 (3,5) 之间最小值, 应该是 3;
单节点更新, 因为 修改某一节点 会影响他的父亲节点, 因此,需要递归依次修改;
/* 单节点更新 idex:待更新节点在array 中下标 */ void update_one(int root,int begin,int end,int idex,int add) { if(begin==end) { if(idex==begin) Seg[root].val+=add; return; } int mid=(begin+end)>>1; if(idex<=mid) update_one(root*2+1,begin,mid,idex,add);//左更新 else update_one(root*2+2,mid+1,end,idex,add);//右更新 Seg[root].val=min(Seg[root*2+1].val,Seg[root*2+2].val); }
例如 我们在 (3,5) 之间 把 3 的值 -2; 从 4变成2; 则 (3,5) 最小值从3 变成2
区间修改:
区间修改 用到一个延迟标记的东西, 很神奇,
做法: 需要在构造数时 添加 一个标记;
区间修改时 添加pash 延时函数;
代码:
struct SegTree{
int val;
int addmark;
}Seg[maxn];
int array[maxn];
void build(int root,int begin,int end)
{
Seg[root].addmark=0;//为区间修改做准备¸
if(begin==end)
Seg[root].val=array[begin];
else
{
int mid=(begin+end)>>1;
build(root*2+1,begin,mid);//左子树
build(root*2+2,mid+1,end);//右子树
Seg[root].val=min(Seg[root*2+1].val,Seg[root*2+2].val);
}
}
void push(int root)
{
if(Seg[root].addmark!=0)
{
Seg[root*2+1].addmark+=Seg[root].addmark;
Seg[root*2+2].addmark+=Seg[root].addmark;
Seg[root*2+1].val+=Seg[root].addmark;
Seg[root*2+2].val+=Seg[root].addmark;
Seg[root].addmark=0;
}
}
int query(int root,int begin,int end,int left,int right) { if(right<begin||left>end) return INF; if(left<=begin&&right>=end) return Seg[root].val; push(root);//区间修改 用到延迟 int mid=(begin+end)>>1; return min(query(root*2+1,begin,mid,left,right),query(root*2+2,mid+1,end,left,right)); }
/* 区间修改 */ void update(int root,int begin,int end,int left,int right,int add) { if(left>end||right<end) return; if(left<=begin&&right>=end) { Seg[root].addmark+=add; Seg[root].val+=add; return; } push(root); int mid=(begin+end)>>1; update(root*2+1,begin,mid,left,right,add); update(root*2+2,mid+1,end,left,right,add); Seg[root].val=min(Seg[root*2+1].val,Seg[root*2+2].val); }
例如 我们将(3,5)所有的值都-2 则 有4-2=2,3-2=1; 最小值为1:
相关文章推荐
- 用线段树求解坐标矩阵中的交并集面积思路及其模板代码
- TF-IDF理解及其Java实现代码实例
- 欧拉函数知识点总结及代码模板及欧拉函数表
- java中IO的基本知识点及其个人理解
- 线段树模板及其详细解释
- 线段树的代码模板
- 欧拉函数知识点总结及代码模板及欧拉函数表
- C#—代码理解知识点(二)
- 线段树的代码模板
- CSharpGL(12)用T4模板生成CSSL及其renderer代码
- JAVA模板方法设计模式(从现实生活角度理解代码原理)
- ACM计算几何知识点详解和模板代码2
- poj 3468..(存线段树个人模板(自敲)pushdown函数还不理解)
- poj 3468..(存线段树个人模板(自敲)pushdown函数还不理解)
- 求树中的最长路 (*【模板】代码个别地方需要根据情况修改 读懂理解后再照搬代码 )
- 线段树模板及其运用
- 线段树的代码模板(补充中)
- 欧拉函数知识点及其求法模板
- 线段树模板以及对线段树的理解
- 线段树解析以及代码模板