线段树——维基百科
来源:互联网 发布:手机淘宝怎么装修店铺 编辑:程序博客网 时间:2024/06/04 00:25
线段树[编辑]
维基百科,自由的百科全书
关于用以储存区间或线段的数据结构,详见“线段树 (储存区间)”。
线段树是一种二叉树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
对于线段树中的每一个非叶子节点[a,b],它的左子树表示的区间为[a,(a+b)/2],右子树表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树。叶节点数目为N,即整个线段区间的长度。
目录
[隐藏]- 1基本操作
- 1.1节点数据向上更新
- 1.2节点懒惰标记下推
- 1.3建树
- 1.4更新
- 1.5区间查询
- 2变种
- 3相关链接
- 4参考资料
基本操作[编辑]
给定整个线段区间,建立一棵线段树的时间复杂度是
。单点修改的时间复杂度是 。单点查询的时间复杂度是 。如果允许惰性赋值而加上延迟标记的话,许多的区间修改的时间复杂度也会是 ,但是单点查询的时间复杂度会变成 。代码中, rt指的是root, 当前子树的根节点; l, r指的是当前子树所统计的区间
利用完全二叉堆的性质来保存节点编号, 所以rt << 1是左子树的节点, rt << 1 | 1是右子树的节点 在查询和成端更新操作中的L和R是指修改或者查询的区间节点数据向上更新[编辑]
将子节点的值更新到父节点。
/* 对于区间求和 */void push_up(int rt) { tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];}/* 对于区间求最大值 */void push_up(int rt) { tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);}
节点懒惰标记下推[编辑]
对于区间求和, 原子数组值需要加上lazy标记乘以子树所统计的区间长度。 len为父节点统计的区间长度, 则len - (len >> 1)为左子树区间长度, len >> 1为右子树区间长度。
void push_down(int rt, int len) { tree[rt << 1] += lazy[rt] * (len - (len >> 1)); lazy[rt << 1] += lazy[rt]; tree[rt << 1 | 1] += lazy[rt] * (len >> 1); lazy[rt << 1 | 1] += lazy[rt]; lazy[rt] = 0;}
对于区间求最大值, 子树的值不需要乘以长度, 所以不需要传递参数len。
void push_down(int rt) { tree[rt << 1] += lazy[rt]; lazy[rt << 1] += lazy[rt]; tree[rt << 1 | 1] += lazy[rt]; lazy[rt << 1 | 1] += lazy[rt]; lazy[rt] = 0;}
建树[编辑]
新建一棵长度N的线段树。
#define lchild rt << 1, l, m#define rchild rt << 1 | 1, m + 1, rvoid build(int rt = 1, int l = 1, int r = N) { if (l == r) { std::cin >> tree[rt]; return; } int m = (l + r) >> 1; build(lchild); build(rchild); push_up(rt);}
更新[编辑]
单点更新, 不需要用到lazy标记
#define lchild rt << 1, l, m#define rchild rt << 1 | 1, m + 1, rvoid update(int p, int delta, int rt = 1, int l = 1, int r = N) { if (l == r) { tree[rt] += delta; return; } int m = (l + r) >> 1; if (p <= m) update(p, delta, lchild); else update(p, delta, rchild); push_up(rt);}
成段更新, 需要用到lazy标记来提高时间效率
#define lchild rt << 1, l, m#define rchild rt << 1 | 1, m + 1, rvoid update(int L, int R, int delta, int rt = 1, int l = 1, int r = N) { if (L <= l && r <= R) { tree[rt] += delta * (r - l + 1); lazy[rt] += delta; return; } if (lazy[rt]) push_down(rt, r - l + 1); int m = (l + r) >> 1; if (L <= m) update(L, R, delta, lchild); if (R > m) update(L, R, delta, rchild); push_up(rt);}
区间查询[编辑]
#define lchild rt << 1, l, m#define rchild rt << 1 | 1, m + 1, rint query(int L, int R, int rt = 1, int l = 1, int r = N) { if (L <= l && r <= R) return tree[rt]; if (lazy[rt]) push_down(rt, r - l + 1); int m = (l + r) >> 1, ret = 0; if (L <= m) ret += query(L, R, lchild); if (R > m) ret += query(L, R, rchild); return ret;}
变种[编辑]
zkw线段树是一种自底向上的线段树,由清华大学的张昆玮提出。它相对于传统线段树的优势体现在减少了递归操作和增加了位运算等操作以减少常数[1]。
相关链接[编辑]
http://dongxicheng.org/structure/segment-tree/
参考资料[编辑]
- ^ 张昆玮. 统计的力量——线段树全接触.
0 0
- 线段树——维基百科
- xargs——维基百科
- 消息队列——维基百科
- Cygwin(转自—维基百科和百度百科)
- ARM架构——转自维基百科
- MASM介绍——摘自维基百科
- 单纯形法——转自维基百科
- C++值的分类 —— 摘自维基百科
- C++引用折叠 —— 摘自维基百科
- 维基百科,全世界的百科全书—Jimmy Wales
- 维基百科搬运 树 (数据结构)
- 维基百科
- 维基百科
- 马尔可夫链-维基百科
- 决策树 维基百科
- RSA--维基百科
- PNG维基百科
- GCompris -- 维基百科
- 在线路设计里“地”有多少种
- Linear Algebra - Lesson 11. 矩阵空间, 秩1矩阵和小世界图
- java 序列化
- 散列/Handler/多线程
- POJ 1201(差分约束+spfa)
- 线段树——维基百科
- UE4资源更新
- 写出高效优美的单片机C语言代码
- 数据库与数据表
- C语言中整型浮点型在计算机中的存储
- Wish:超牛的wish采集工具,挖掘wish热门标签,打造wish爆款软件
- zzu数学 实验十一最速降线
- 终端分屏软件 tmux简单教程
- 2016 ccpc 合肥 HDU5965 扫雷