用于查询数列信息的数据结构，請見「**线段树 (区间查询)**」。

**線段樹**（英語：）是一種二元樹形資料結構，1977年由Jon Louis Bentley發明，用以儲存區間或線段，並且允許快速查詢結構內包含某一點的所有區間。

一個包含n個區間的線段樹，空間複雜度為O(n)，查詢的時間複雜度則為{\displaystyle O(\log n+k)}，其中k是符合條件的區間數量。

此資料結構亦可推廣到高維度。

結構
--

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

本處以一維的線段樹為例。

線段樹結構示意，其儲存的線段顯示在圖片的下方。
令S是一維線段的集合。將這些線段的端點坐標由小到大排序，令其為{\displaystyle x\_{1},x\_{2},\cdots ,x\_{m}}。我們將被這些端點切分的每一個區間稱為「單位區間」（每個端點所在的位置會單獨成為一個單位區間），從左到右包含：

{\displaystyle (-\infty ,x\_{1}),[x\_{1},x\_{1}],(x\_{1},x\_{2}),[x\_{2},x\_{2}],...(x\_{m-1},x\_{m}),[x\_{m},x\_{m}],(x\_{m},+\infty )}
線段樹的結構為一個二元樹，每個節點都代表一個坐標區間，節點N所代表的區間記為Int(N)，則其需符合以下條件：

* 其每一個葉節點，從左到右代表每個單位區間。
* 其內部節點代表的區間是其兩個兒子代表的區間之聯集。
* 每個節點（包含葉子）中有一個儲存線段的資料結構。若一個線段S的坐標區間包含Int(N)但不包含Int(parent(N))，則節點N中會儲存線段S。

實現
--

### C++

在此以求出範圍最小值作為範例

```
template <typename T>
class SegMinTree {
 public:
 // 新建一个最小值线段树用于处理[0, n)的数据
 SegMinTree(int n) : N(n), values\_(4 \* n), deltas\_(4 \* n) {}

 // 返回指定位置的数据
 T Get(int index) const {
 return GetRangeMin(index, index + 1);
 }

 // 将数据写入指定位置
 void Set(int index, T value) {
 IncrementRange(index, index + 1, value - Get(index));
 }

 // 返回区间上的最小值
 T GetRangeMin(int start, int end) const {
 return Query(FullSegment(), start, end);
 }

 // 对一段区间上的所有值加上同一个增量
 void IncrementRange(int start, int end, T delta) {
 Increment(FullSegment(), start, end, delta);
 }

 private:
 struct Segment {
 int id;
 int start;
 int end;

 bool Overlaps(int start, int end) const {
 return this->start < end && this->end > start;
 }

 bool IsIn(int start, int end) const {
 return start <= this->start && this->end <= end;
 }

 Segment Left() const {
 return {.id = id \* 2, .start = start, .end = (start + end + 1) / 2};
 }

 Segment Right() const {
 return {.id = id \* 2 + 1, .start = (start + end + 1) / 2, .end = end};
 }
 };

 Segment FullSegment() const {
 return {.id = 1, .start = 0, .end = N};
 }

 T Query(const Segment& segment, int start, int end) const {
 if (!segment.Overlaps(start, end)) {
 return std::numeric\_limits<T>::max();
 }
 if (segment.IsIn(start, end)) {
 return values\_[segment.id];
 }
 // 处理部分重合的情况
 T children\_value = std::min(Query(segment.Left(), start, end), Query(segment.Right(), start, end));
 return deltas\_[segment.id] + children\_value;
 }

 // 返回segment里面新的最小值(跟[start, end)无关).
 T Increment(const Segment& segment, int start, int end, T delta) {
 if (!segment.Overlaps(start, end)) {
 return values\_[segment.id]; // 没有改变
 }
 if (segment.IsIn(start, end)) {
 values\_[segment.id] += delta;
 deltas\_[segment.id] += delta;
 return values\_[segment.id];
 }
 // 处理部分重合的情况
 T value = std::min(Increment(segment.Left(), start, end, delta),
 Increment(segment.Right(), start, end, delta));
 value += deltas\_[segment.id];
 values\_[segment.id] = value;
 return value; 
 }

 const int N;
 std::vector<T> values\_;
 std::vector<T> deltas\_; // deltas\_[id] 里的值只用于子结点。
};

```

### C

在此以求出範圍最小值作為範例

```
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n , q , seg[800005] = {0} , x , a , b , arr[200005];
 
//初始化線段樹
void build(int id , int l , int r)
{
 if(l == r){
 seg[id] = arr[l];
 return;
 }
 int mid = (l+r)>>1;
 build(2\*id,l,mid);
 build(2\*id+1,mid+1,r);
 seg[id] = min(seg[2\*id],seg[2\*id+1]);
 return;
}
 
//從線段樹中提取資訊
int query(int id , int l , int r , int ql , int qr)
{
 if(qr < l || ql > r)return 1e9;
 if(ql <= l && r <= qr)return seg[id];
 int mid = (l+r)>>1;
 return min(query(2\*id,l,mid,ql,qr),
 query(2\*id+1,mid+1,r,ql,qr));
}
 
//更新線段樹
void update(int id , int l , int r , int k , int u)
{
 if(l==r){
 seg[id] = u;
 return;
 }
 int mid = (l+r)>>1;
 if(k <= mid)update(2\*id,l,mid,k,u);
 else update(2\*id+1,mid+1,r,k,u);
 seg[id] = min(seg[2\*id] , seg[2\*id+1]);
 return;
}

signed main()
{
 //輸入陣列大小 提問數量
 cin >> n >> q;
 for(int i = 1 ; i <= n ; i++)cin >> arr[i];
 build(1,1,n);
 for(int i = 0 ; i < q ; i++)
 {
 //輸入 1 a b 代表將第a個數改為b
 //輸入 2 a b 代表求[a,b]中的最小值
 cin >> x >> a >> b;
 if(x == 1)update(1,1,n,a,b);
 else if(x == 2)cout << query(1,1,n,a,b) << "\n";
 }
 return 0;
}

```

參考資料
----

1. ↑  （de Berg 等人 2000，p.229）

* de Berg, Mark; van Kreveld, Marc; Overmars, Mark; Schwarzkopf, Otfried.. 需要免费注册 2nd. Springer-Verlag Berlin Heidelberg New York. 20002000. ISBN 3-540-65620-0. doi:10.1007/978-3-540-77974-2.  含有內容需登入查看的頁面 (link)
* （页面存档备份，存于）

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
计算机科学中的树 |
| --- |
|  |
| 二叉树 | 
* 二元搜尋樹
* 笛卡尔树
* MVP树
* Top tree（英语：）
* T树
* 线索二叉树

 |
|  |
| 自平衡二叉查找树 | 
* AA树
* AVL树
* 左倾红黑树
* 红黑树
* 替罪羊树
* 伸展树
* 树堆
* 加权平衡树

 |
|  |
| B树 | 
* B+树
* B\*树
* Bx树
* UB树
* 2-3树
* 2-3-4树
* (a,b)-树
* 跳舞樹（英语：）
* H树

 |
|  |
| 堆 | 
* 二叉堆
* 二项堆
* 斐波那契堆
* 左偏树
* 配对堆
* 斜堆
* 范恩德蟒蛇樹（英语：）

 |
|  |
| Trie | 
* 后缀树
* 基数树
* 三叉查找树
* X-快速前缀树
* Y-快速前缀树
* AC自动机

 |
|  |
| 二叉空间分割（BSP）树 | 
* 四叉树
* 八叉树
* *k*-d树
* 隐式*k*-d树
* VP树

 |
|  |
| 非二叉树 | 
* 指数树（英语：）
* 融合树（英语：）
* PQ树（英语：）
* SPQR树（英语：）

 |
|  |
| 空间数据分割树 | 
* R树
* R\*树
* R+树
* X树
* M树
* 線段樹 (儲存區間)
* 线段树 (区间查询)
	+ 可持久化线段树
* 希尔伯特R树
* 优先R树

 |
|  |
| 其他树 | 
* 散列日历
* 散列树
* 手指樹（英语：）
* 顺序统计树
* 度量樹（英语：）
* 覆蓋樹（英语：）
* BK树
* 二重連鎖樹（英语：）
* iDistance（英语：）
* Link-cut tree（英语：）
* Log-structured merge-tree（英语：）
* 树状数组
* 哈希树

 |

 |