fbpx
维基百科

线段树 (区间查询)

线段树是一种二元樹,可視為树状数组的變種,最早出現在2001年,由算法競賽選手發明。

此資料結構實際應用用途不大,但由於其程式易於實作而被廣泛應用於算法競賽當中。其用途是在查詢一個指定區間內的資訊,並可在同樣的時間複雜度支援更新等操作。

結構 编辑

線段樹是一個平衡的二叉树,它将每个长度不为1的区间划分成左右两个区间递归求解。令整個區間的長度為N,則其有N個葉節點,每個葉節點代表一個單位區間,每個內部結點代表的區間為其兩個兒子代表區間的聯集。这种数据结构可以方便的进行大部分的区间操作。


基本操作 编辑

線段樹所要提供的是查詢一個區間 內的資訊 ,並允許修改操作。要使用線段樹,此資訊必須滿足對於區間 與位於區間內的一點  要可以由  求得。例如範圍最值查詢即符合此條件。线段树维护的信息在很多时候可以认为是满足含半群的性质的信息。

代码中, 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, r void 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, r void 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标记来提高时间效率。 如果要求修改区间,把所有包含在区间中的节点都遍历一次、修改一次,时间复杂度太高。所以要有 「懒惰标记」(lazy标记) 。 懒惰标记就是通过延迟对节点信息的更改,从而减少不必要的操作次数。每次执行修改时,通过打标记的方法表明该节点对应的区间在某一次操作中被更改,但不更新该节点的子节点的信息,实质性的修改则在下一次访问带有标记的节点时才进行。

#define lchild rt << 1, l, m #define rchild rt << 1 | 1, m + 1, r void 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, r int 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; } 

可直接使用的编程模板 编辑

#include <iostream> #include <cstdio> #include <cmath> #include <cstring> using namespace std; namespace SegTree {  #define maxn 1000000  class SegmentTree {  #define lson (o<<1)  #define rson (o<<1|1)  #define mid ((l+r)>>1)  public :  int addv[maxn], maxv[maxn], minv[maxn], sumv[maxn];  int arr[maxn];  int N;  private:int _max(const int& _, const int& __) { return _>__?_:__; }  private:int _min(const int& _, const int& __) { return _<__?_:__; }  public : int pushup(int o) {  minv[o] = _min(minv[lson], minv[rson]);  maxv[o] = _max(maxv[lson], maxv[rson]);  sumv[o] = sumv[lson] + sumv[rson];  return 0;  }  public : int pushdown(int o, int l, int r) {  if(addv[o] == 0) return -1;  addv[lson] += addv[o]; addv[rson] += addv[o];  minv[lson] += addv[o]; minv[rson] += addv[o];  maxv[lson] += addv[o]; maxv[rson] += addv[o];  sumv[lson] += addv[o] * (mid-l+1); sumv[rson] += addv[o] * (r-mid);  addv[o] = 0;  }  public : int Build(int o, int l, int r) {  addv[o] = 0;  if(l == r) {  maxv[o] = arr[l];minv[o] = arr[l];sumv[o] = arr[l];  return 0;  }  Build(lson, l, mid);  Build(rson, mid+1, r);  pushup(o);  return 0;  }  public : int optadd(int o, int l, int r, int ql, int qr, int addval) {  if(ql > r or qr < l) return 0;  if(ql <= l and r <= qr) {  addv[o] += addval;  sumv[o] += addval * (r-l+1);  return 0;  }  pushdown(o, l, r);  optadd(lson, l, mid, ql, qr, addval);  optadd(rson, mid+1, r, ql, qr, addval);  pushup(o);  }  public : int query_sum(int o, int l, int r, int ql, int qr) {  if(ql > r or qr < l) return 0;  if(ql <= l and r <= qr) {  return sumv[o];  }  pushdown(o, l, r);  return query_sum(lson, l, mid, ql, qr) + query_sum(rson, mid+1, r, ql, qr);  }  public : int query_min(int o, int l, int r, int ql, int qr) {  if(ql > r or qr < l) return 0;  if(ql <= l and r <= qr) {  return minv[o];  }  pushdown(o, l, r);  return _min(query_min(lson, l, mid, ql, qr), query_min(rson, mid+1, r, ql, qr));  }  public : int query_max(int o, int l, int r, int ql, int qr) {  if(ql > r or qr < l) return 0;  if(ql <= l and r <= qr) {  return maxv[o];  }  pushdown(o, l, r);  return _max(query_max(lson, l, mid, ql, qr), query_max(rson, mid+1, r, ql, qr));  }  }; }  //End of SegmentTree using namespace SegTree; 

变种 编辑

zkw线段树是一种自底向上的线段树,由清华大学的张昆玮提出。它相对于传统线段树的优势体现在减少了递归操作和增加了位运算等操作以减少常数[1]

相关链接 编辑

http://dongxicheng.org/structure/segment-tree/ (页面存档备份,存于互联网档案馆https://cp-algorithms.com/data_structures/segment_tree.html (页面存档备份,存于互联网档案馆) Segment Tree – CP-Algorithms

參考資料 编辑

  1. ^ 张昆玮. 统计的力量——线段树全接触. [2014-07-20]. (原始内容于2021-11-04). 

线段树, 区间查询, 关于用以儲存區間或線段的資料結構, 区间树, 請見, 線段樹, 儲存區間, 此條目可能包含原创研究, 2017年3月10日, 请协助補充参考资料, 添加相关内联标签和删除原创研究内容以改善这篇条目, 详细情况请参见讨论页, 此條目需要补充更多来源, 2017年3月10日, 请协助補充多方面可靠来源以改善这篇条目, 无法查证的内容可能會因為异议提出而被移除, 致使用者, 请搜索一下条目的标题, 来源搜索, 网页, 新闻, 书籍, 学术, 图像, 以检查网络上是否存在该主题的更多可靠来源, 判定指. 关于用以儲存區間或線段的資料結構 区间树 請見 線段樹 儲存區間 此條目可能包含原创研究 2017年3月10日 请协助補充参考资料 添加相关内联标签和删除原创研究内容以改善这篇条目 详细情况请参见讨论页 此條目需要补充更多来源 2017年3月10日 请协助補充多方面可靠来源以改善这篇条目 无法查证的内容可能會因為异议提出而被移除 致使用者 请搜索一下条目的标题 来源搜索 线段树 区间查询 网页 新闻 书籍 学术 图像 以检查网络上是否存在该主题的更多可靠来源 判定指引 线段树是一种二元樹 可視為树状数组的變種 最早出現在2001年 由算法競賽選手發明 此資料結構實際應用用途不大 但由於其程式易於實作而被廣泛應用於算法競賽當中 其用途是在O log N displaystyle O log N 查詢一個指定區間內的資訊 並可在同樣的時間複雜度支援更新等操作 目录 1 結構 2 基本操作 2 1 节点数据向上更新 2 2 节点懒惰标记下推 2 3 建树 2 4 更新 2 5 区间查询 2 6 可直接使用的编程模板 3 变种 4 相关链接 5 參考資料結構 编辑線段樹是一個平衡的二叉树 它将每个长度不为1的区间划分成左右两个区间递归求解 令整個區間的長度為N 則其有N個葉節點 每個葉節點代表一個單位區間 每個內部結點代表的區間為其兩個兒子代表區間的聯集 这种数据结构可以方便的进行大部分的区间操作 基本操作 编辑線段樹所要提供的是查詢一個區間 l r displaystyle l r nbsp 內的資訊f l r displaystyle f l r nbsp 並允許修改操作 要使用線段樹 此資訊必須滿足對於區間 l r displaystyle l r nbsp 與位於區間內的一點m displaystyle m nbsp f l r displaystyle f l r nbsp 要可以由f l m displaystyle f l m nbsp f m r displaystyle f m r nbsp 求得 例如範圍最值查詢即符合此條件 线段树维护的信息在很多时候可以认为是满足含半群的性质的信息 代码中 rt指的是root 当前子树的根节点 l r指的是当前子树所统计的区间 l r displaystyle l r nbsp 利用完全二叉堆的性质来保存节点编号 所以rt lt lt 1是左子树的节点 rt lt lt 1 1是右子树的节点在查询和成端更新操作中的L和R是指修改或者查询的区间 节点数据向上更新 编辑 将子节点的值更新到父节点 对于区间求和 void push up int rt tree rt tree rt lt lt 1 tree rt lt lt 1 1 对于区间求最大值 void push up int rt tree rt max tree rt lt lt 1 tree rt lt lt 1 1 节点懒惰标记下推 编辑 对于区间求和 原子数组值需要加上lazy标记乘以子树所统计的区间长度 len为父节点统计的区间长度 则len len gt gt 1 为左子树区间长度 len gt gt 1为右子树区间长度 void push down int rt int len tree rt lt lt 1 lazy rt len len gt gt 1 lazy rt lt lt 1 lazy rt tree rt lt lt 1 1 lazy rt len gt gt 1 lazy rt lt lt 1 1 lazy rt lazy rt 0 对于区间求最大值 子树的值不需要乘以长度 所以不需要传递参数len void push down int rt tree rt lt lt 1 lazy rt lazy rt lt lt 1 lazy rt tree rt lt lt 1 1 lazy rt lazy rt lt lt 1 1 lazy rt lazy rt 0 建树 编辑 新建一棵长度N的线段树 define lchild rt lt lt 1 l m define rchild rt lt lt 1 1 m 1 r void build int rt 1 int l 1 int r N if l r std cin gt gt tree rt return int m l r gt gt 1 build lchild build rchild push up rt 更新 编辑 单点更新 不需要用到lazy标记 define lchild rt lt lt 1 l m define rchild rt lt lt 1 1 m 1 r void 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 gt gt 1 if p lt m update p delta lchild else update p delta rchild push up rt 成段更新 需要用到lazy标记来提高时间效率 如果要求修改区间 把所有包含在区间中的节点都遍历一次 修改一次 时间复杂度太高 所以要有 懒惰标记 lazy标记 懒惰标记就是通过延迟对节点信息的更改 从而减少不必要的操作次数 每次执行修改时 通过打标记的方法表明该节点对应的区间在某一次操作中被更改 但不更新该节点的子节点的信息 实质性的修改则在下一次访问带有标记的节点时才进行 define lchild rt lt lt 1 l m define rchild rt lt lt 1 1 m 1 r void update int L int R int delta int rt 1 int l 1 int r N if L lt l amp amp r lt R tree rt delta r l 1 lazy rt delta return if lazy rt push down rt r l 1 int m l r gt gt 1 if L lt m update L R delta lchild if R gt m update L R delta rchild push up rt 区间查询 编辑 define lchild rt lt lt 1 l m define rchild rt lt lt 1 1 m 1 r int query int L int R int rt 1 int l 1 int r N if L lt l amp amp r lt R return tree rt if lazy rt push down rt r l 1 int m l r gt gt 1 ret 0 if L lt m ret query L R lchild if R gt m ret query L R rchild return ret 可直接使用的编程模板 编辑 include lt iostream gt include lt cstdio gt include lt cmath gt include lt cstring gt using namespace std namespace SegTree define maxn 1000000 class SegmentTree define lson o lt lt 1 define rson o lt lt 1 1 define mid l r gt gt 1 public int addv maxn maxv maxn minv maxn sumv maxn int arr maxn int N private int max const int amp const int amp return gt private int min const int amp const int amp return lt public int pushup int o minv o min minv lson minv rson maxv o max maxv lson maxv rson sumv o sumv lson sumv rson return 0 public int pushdown int o int l int r if addv o 0 return 1 addv lson addv o addv rson addv o minv lson addv o minv rson addv o maxv lson addv o maxv rson addv o sumv lson addv o mid l 1 sumv rson addv o r mid addv o 0 public int Build int o int l int r addv o 0 if l r maxv o arr l minv o arr l sumv o arr l return 0 Build lson l mid Build rson mid 1 r pushup o return 0 public int optadd int o int l int r int ql int qr int addval if ql gt r or qr lt l return 0 if ql lt l and r lt qr addv o addval sumv o addval r l 1 return 0 pushdown o l r optadd lson l mid ql qr addval optadd rson mid 1 r ql qr addval pushup o public int query sum int o int l int r int ql int qr if ql gt r or qr lt l return 0 if ql lt l and r lt qr return sumv o pushdown o l r return query sum lson l mid ql qr query sum rson mid 1 r ql qr public int query min int o int l int r int ql int qr if ql gt r or qr lt l return 0 if ql lt l and r lt qr return minv o pushdown o l r return min query min lson l mid ql qr query min rson mid 1 r ql qr public int query max int o int l int r int ql int qr if ql gt r or qr lt l return 0 if ql lt l and r lt qr return maxv o pushdown o l r return max query max lson l mid ql qr query max rson mid 1 r ql qr End of SegmentTree using namespace SegTree 变种 编辑zkw线段树是一种自底向上的线段树 由清华大学的张昆玮提出 它相对于传统线段树的优势体现在减少了递归操作和增加了位运算等操作以减少常数 1 相关链接 编辑http dongxicheng org structure segment tree 页面存档备份 存于互联网档案馆 https cp algorithms com data structures segment tree html 页面存档备份 存于互联网档案馆 Segment Tree CP Algorithms參考資料 编辑 张昆玮 统计的力量 线段树全接触 2014 07 20 原始内容存档于2021 11 04 取自 https zh wikipedia org w index php title 线段树 区间查询 amp oldid 79024975, 维基百科,wiki,书籍,书籍,图书馆,

文章

,阅读,下载,免费,免费下载,mp3,视频,mp4,3gp, jpg,jpeg,gif,png,图片,音乐,歌曲,电影,书籍,游戏,游戏。