线段树是常用的维护区间信息的数据结构
线段树支持操作
(1)单点修改
(2)区间修改
(3)区间查询(区间求和,求区间max,求区间min,区间gcd等等)
线段树特点:
(1)每个节点表示一个区间
(2)root node表示区间为[1, n]
(3)leaf Node表示区间为[x, x]
(4)线段树中如果一个节点区间为[l, r](l != r)那么这个节点的左子树的根表示区间为[l, mid],这个节点的右子树的根表示区间就是[mid + 1, r], 其中mid = Floor((l + r) /2)
线段树特点:
(1)每个节点表示一个区间
(2)root node表示区间为[1, n]
(3)leaf Node表示区间为[x, x]
(4)线段树中如果一个节点区间为[l, r](l != r)那么这个节点的左子树的根表示区间为[l, mid],这个节点的右子树的根表示区间就是[mid + 1, r], 其中mid = Floor((l + r) /2)
线段树的存储方式

建立线段树
时间复杂度为O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| struct Segment_Tree { int l,r; info_type info; tag_type tag; }tr[4 * N];
info_type operator + (info_type x,info_type y) { } info_type opreator + (info_type x,tag_type y) { } tag_type operator + (tag_type x,tag_type y) { } void opt (info_type &x,tag_type y) { x = x + y; } void push_up (int u) {
tr[u].info = tr[u << 1].info + tr[u << 1 | 1].info; } void build (int u,int l,int r) { if (l == r) { tr[u] = {l,r,info (a[l])}; return ; } tr[u] = {l,r}; int mid = l + r >> 1; build (u << 1,l,mid),build (u << 1 | 1,mid + 1,r); push_up (u); }
|
单点修改
时间复杂度均为O(logN)
1 2 3 4 5 6 7 8 9 10 11
| void modify (int u,int x,tag_type d) { if (tr[u].l == tr[u].r) { tr[u].info += d; return ; } int mid = tr[u].l + tr[u].r >> 1; if (x <= mid) modify (u << 1,x,d); else modify (u << 1 | 1,x,d); push_up (u) }
|
区间修改
思路1:
时间复杂度均为O(N)
1 2 3 4 5 6 7 8 9 10
| void modify (int u,int l,int r,tag_type d) { if (tr[u].l == tr[u].r) { tr[u].info += d; return ; } int mid = tr[u].l + tr[u].r >> 1; if (l <= mid) modify (u << 1,l,r,d); if (r >= mid + 1) modify (u << 1 | 1,l,r,d); push_up (u); }
|
思路2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void push_down (int u) { if (tr[u].tag) { tr[u << 1].info += tr[u].tag,tr[u << 1 | 1].info += tr[u].tag tr[u].tag = info (); } } void modify (int u,int l,int r,int d) { if (l <= tr[u].l && tr[u].r <= r) { opt (u,d); return ; } push_down (u); int mid = tr[u].l + tr[u].r >> 1; if (l <= mid) modify (u << 1,l,r,d); if (r >= mid + 1) modify (u << 1 | 1,l,r,d); push_up (u); }
|
区间查询
1 2 3 4 5 6 7 8 9
| LL query_sum (int u,int l,int r) { if (l <= tr[u].l && tr[u].r <= r) return tr[u].sum; push_down (u); int mid = tr[u].l + tr[u].r >> 1; LL sum = 0; if (l <= mid) sum += query_sum (u << 1,l,r); if (r >= mid + 1) sum += query_sum (u << 1 | 1,l,r); return sum; }
|
参考:线段树 acwing
个人实现:
https://github.com/jingtao8a/leetcode/blob/master/src/main/java/org/jingtao8a/code_random_notes/SegmentTree.java