死線斷裂術
建北電資小社課 演算法
線段樹
從中間切一半
1
5
5
8
4
3
7
2
6
13
7
9
19
16
35
\(n\) 代表資料長度
1
5
8
4
3
7
2
1
13
7
9
14
16
30
\(n\) 代表資料長度
線段樹 | 前綴和 | 差分 | |
---|---|---|---|
建表 |
|
||
區間查詢 | |||
單點修改 | |||
區間修改 |
\(O(n)\)
\(O(n)\)
\(O(log(n))\)
\(O(1)\)
\(O(n)\)
\(O(log(n))\)
\(O(n)\)
\(O(1)\)
\(O(log(n))\)
\(O(n)\)
\(O(1)\)
struct node{
int value;
node *left, *right;
};
int n;
vector<int> data(n*4,0);
註: \(n\) 代表原資料長度
註: \(n\) 代表原資料長度
. . .
註: \(n\) 代表原資料長度
註: \(n\) 代表原資料長度
int n;
vector<int> data(n*2,0);
註: \(n\) 代表原資料長度
有點複雜等等再說
Create
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
int n;
vector<int> nodes;
SegmentTree()
: n(0) {
nodes.clear();
}
SegmentTree(vector<int> &data)
: n(data.size()) {
nodes.resize(n * 4);
build(data, 0, n);
}
void build(const vector<int> &data, int l, int r, int it = 1) {
if (r - l == 1) {
nodes[it] = data[l];
return;
}
int mid = (l + r) / 2;
build(data, l, mid, it * 2);
build(data, mid, r, it * 2 + 1);
nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
return;
}
};
# PRESENTING CODE
初始化 nodes 大小
宣告 變數
到葉節點 複製值進去
取得中間值 區分左右子節點的區間
左區間在 \(x \times 2\)
右區間在 \(x \times 2 +1\)
遞迴計算子節點值
結合左右子節點值 (pull)
遞迴函式
還沒到葉節點
變數解釋
\(n\) 資料長度
\(nodes\) 節點值
\(data\) 原始資料
\(l\) \(r\) 左右邊界
\(mid\) 中介: 區分左右子區間
\(it\) 偽指標: 指向結點在\(nodes\)的位置
註: 皆為左閉右開 0-base
Read
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
int n;
vector<int> nodes;
SegmentTree()
: n(0) {
nodes.clear();
}
SegmentTree(vector<int> &data)
: n(data.size()) {
nodes.resize(n * 4);
build(data, 0, n);
}
void build(const vector<int> &data, int l, int r, int it = 1) {
if (r - l == 1) {
nodes[it] = data[l];
return;
}
int mid = (l + r) / 2;
build(data, l, mid, it * 2);
build(data, mid, r, it * 2 + 1);
nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
return;
}
int query(int queryL, int queryR, int l, int r, int it = 1) {
if (queryL == l && queryR == r) return nodes[it];
int mid = (l + r) / 2;
if (queryR <= mid)
return query(queryL, queryR, l, mid, it * 2);
else if (queryL >= mid)
return query(queryL, queryR, mid, r, it * 2 + 1);
else return query(queryL, mid, l, mid,
it * 2) +
query(mid, queryR, mid, r,
it * 2 + 1);
}
};
# PRESENTING CODE
已經找到了
取得中間值 區分左右節點範圍
完全在左
完全在右
左右都有
左右都有
注意範圍
變數解釋
\(nodes\) 節點值
\(queryL\) \(queryR\) 搜尋左右界
\(l\) \(r\) 左右邊界
\(mid\) 中介: 區分左右子區間
\(it\) 偽指標: 指向結點在\(nodes\)的位置
註: 皆為左閉右開 0-base
Update
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
int n;
vector<int> nodes;
SegmentTree()
: n(0) {
nodes.clear();
}
SegmentTree(vector<int> &data)
: n(data.size()) {
nodes.resize(n * 4);
build(data, 0, n);
}
void build(const vector<int> &data, int l, int r, int it = 1) {
if (r - l == 1) {
nodes[it] = data[l];
return;
}
int mid = (l + r) / 2;
build(data, l, mid, it * 2);
build(data, mid, r, it * 2 + 1);
nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
return;
}
int query(int queryL, int queryR, int l, int r, int it = 1) {
if (queryL == l && queryR == r) return nodes[it];
int mid = (l + r) / 2;
if (queryR <= mid)
return query(queryL, queryR, l, mid, it * 2);
else if (queryL >= mid)
return query(queryL, queryR, mid, r, it * 2 + 1);
else return query(queryL, mid, l, mid,
it * 2) +
query(mid, queryR, mid, r,
it * 2 + 1);
}
void modify1(int pos, int modifyValue, int l, int r, int it = 1) {
if (r - l == 1) {
nodes[it] = modifyValue;
return;
}
int mid = (l + r) / 2;
if (pos < mid) modify1(pos, modifyValue, l, mid, it * 2);
else modify1(pos, modifyValue, mid, r, it * 2 + 1);
// pull
nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
return;
}
};
# PRESENTING CODE
已經找到了 直接修改
取得中間值 區分左右節點範圍
在左
在右
修改目前節點值 (pull)
註: 皆為左閉右開 0-base
變數解釋
\(nodes\) 節點值
\(queryL\) \(queryR\) 搜尋左右界
\(l\) \(r\) 左右邊界
\(mid\) 中介: 區分左右子區間
\(it\) 偽指標: 指向結點在\(nodes\)的位置
Update
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
int n;
vector<int> nodes;
vector<int> tags;
SegmentTree()
: n(0) {
nodes.clear();
tags.clear();
}
SegmentTree(vector<int> &data)
: n(data.size()) {
nodes.resize(n * 4);
tags.resize(n * 4);
build(data, 0, n);
}
void build(const vector<int> &data, int l, int r, int it = 1) {
if (r - l == 1) {
nodes[it] = data[l];
return;
}
int mid = (l + r) / 2;
build(data, l, mid, it * 2);
build(data, mid, r, it * 2 + 1);
nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
return;
}
void push(int l, int r, int it) {
int mid = (l + r) / 2;
nodes[it * 2] += tags[it] * (mid - l);
nodes[it * 2 + 1] += tags[it] * (r - mid);
tags[it * 2] += tags[it];
tags[it * 2 + 1] += tags[it];
tags[it] = 0;
return;
}
int query(int queryL, int queryR, int l, int r, int it = 1) {
if (queryL == l && queryR == r) return nodes[it];
push(l, r, it);
int mid = (l + r) / 2;
if (queryR <= mid)
return query(queryL, queryR, l, mid, it * 2);
else if (queryL >= mid)
return query(queryL, queryR, mid, r, it * 2 + 1);
else return query(queryL, mid, l, mid,
it * 2) +
query(mid, queryR, mid, r,
it * 2 + 1);
}
void modify(int modifyL, int modifyR, int delta,
int l, int r, int it = 1) {
if (modifyL == l && modifyR == r) {
nodes[it] += delta * (r - l);
tags[it] += delta;
return;
}
push(l, r, it);
int mid = (l + r) / 2;
if (modifyR <= mid)
modify(modifyL, modifyR, delta, l, mid, it * 2);
else if (modifyL >= mid)
modify(modifyL, modifyR, delta, mid, r, it * 2 + 1);
else {
modify(modifyL, mid, delta, l, mid, it * 2);
modify(mid, modifyR, delta, mid, r, it * 2 + 1);
}
// pull
nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
return;
}
};
# PRESENTING CODE
新增宣告 tags (懶標)
build 函式 不需要改
註: 皆為左閉右開 0-base
變數解釋
\(nodes\) 節點值
\(tags\) 懶標值
\(l\) \(r\) 左右邊界
\(mid\) 中介: 區分左右子區間
\(it\) 偽指標: 指向結點在\(nodes\)的位置
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
int n;
vector<int> nodes;
vector<int> tags;
SegmentTree()
: n(0) {
nodes.clear();
tags.clear();
}
SegmentTree(vector<int> &data)
: n(data.size()) {
nodes.resize(n * 4);
tags.resize(n * 4);
build(data, 0, n);
}
void build(const vector<int> &data, int l, int r, int it = 1) {
if (r - l == 1) {
nodes[it] = data[l];
return;
}
int mid = (l + r) / 2;
build(data, l, mid, it * 2);
build(data, mid, r, it * 2 + 1);
nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
return;
}
void push(int l, int r, int it) {
int mid = (l + r) / 2;
nodes[it * 2] += tags[it] * (mid - l);
nodes[it * 2 + 1] += tags[it] * (r - mid);
tags[it * 2] += tags[it];
tags[it * 2 + 1] += tags[it];
tags[it] = 0;
return;
}
int query(int queryL, int queryR, int l, int r, int it = 1) {
if (queryL == l && queryR == r) return nodes[it];
push(l, r, it);
int mid = (l + r) / 2;
if (queryR <= mid)
return query(queryL, queryR, l, mid, it * 2);
else if (queryL >= mid)
return query(queryL, queryR, mid, r, it * 2 + 1);
else return query(queryL, mid, l, mid,
it * 2) +
query(mid, queryR, mid, r,
it * 2 + 1);
}
void modify(int modifyL, int modifyR, int delta,
int l, int r, int it = 1) {
if (modifyL == l && modifyR == r) {
nodes[it] += delta * (r - l);
tags[it] += delta;
return;
}
push(l, r, it);
int mid = (l + r) / 2;
if (modifyR <= mid)
modify(modifyL, modifyR, delta, l, mid, it * 2);
else if (modifyL >= mid)
modify(modifyL, modifyR, delta, mid, r, it * 2 + 1);
else {
modify(modifyL, mid, delta, l, mid, it * 2);
modify(mid, modifyR, delta, mid, r, it * 2 + 1);
}
// pull
nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
return;
}
};
# PRESENTING CODE
需要知道 子節點範圍
左右子節點都加上
\(懶標 \times 範圍大小\)
更新子節點懶標
懶標歸0
push 完成
註: 皆為左閉右開 0-base
變數解釋
\(nodes\) 節點值
\(tags\) 懶標值
\(l\) \(r\) 左右邊界
\(mid\) 中介: 區分左右子區間
\(it\) 偽指標: 指向結點在\(nodes\)的位置
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
int n;
vector<int> nodes;
vector<int> tags;
SegmentTree()
: n(0) {
nodes.clear();
tags.clear();
}
SegmentTree(vector<int> &data)
: n(data.size()) {
nodes.resize(n * 4);
tags.resize(n * 4);
build(data, 0, n);
}
void build(const vector<int> &data, int l, int r, int it = 1) {
if (r - l == 1) {
nodes[it] = data[l];
return;
}
int mid = (l + r) / 2;
build(data, l, mid, it * 2);
build(data, mid, r, it * 2 + 1);
nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
return;
}
void push(int l, int r, int it) {
int mid = (l + r) / 2;
nodes[it * 2] += tags[it] * (mid - l);
nodes[it * 2 + 1] += tags[it] * (r - mid);
tags[it * 2] += tags[it];
tags[it * 2 + 1] += tags[it];
tags[it] = 0;
return;
}
int query(int queryL, int queryR, int l, int r, int it = 1) {
if (queryL == l && queryR == r) return nodes[it];
push(l, r, it);
int mid = (l + r) / 2;
if (queryR <= mid)
return query(queryL, queryR, l, mid, it * 2);
else if (queryL >= mid)
return query(queryL, queryR, mid, r, it * 2 + 1);
else return query(queryL, mid, l, mid,
it * 2) +
query(mid, queryR, mid, r,
it * 2 + 1);
}
void modify(int modifyL, int modifyR, int delta,
int l, int r, int it = 1) {
if (modifyL == l && modifyR == r) {
nodes[it] += delta * (r - l);
tags[it] += delta;
return;
}
push(l, r, it);
int mid = (l + r) / 2;
if (modifyR <= mid)
modify(modifyL, modifyR, delta, l, mid, it * 2);
else if (modifyL >= mid)
modify(modifyL, modifyR, delta, mid, r, it * 2 + 1);
else {
modify(modifyL, mid, delta, l, mid, it * 2);
modify(mid, modifyR, delta, mid, r, it * 2 + 1);
}
// pull
nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
return;
}
};
# PRESENTING CODE
每次到了就下推
註: 皆為左閉右開 0-base
變數解釋
\(nodes\) 節點值
\(queryL\) \(queryR\) 搜尋左右界
\(l\) \(r\) 左右邊界
\(mid\) 中介: 區分左右子區間
\(it\) 偽指標: 指向結點在\(nodes\)的位置
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
int n;
vector<int> nodes;
vector<int> tags;
SegmentTree()
: n(0) {
nodes.clear();
tags.clear();
}
SegmentTree(vector<int> &data)
: n(data.size()) {
nodes.resize(n * 4);
tags.resize(n * 4);
build(data, 0, n);
}
void build(const vector<int> &data, int l, int r, int it = 1) {
if (r - l == 1) {
nodes[it] = data[l];
return;
}
int mid = (l + r) / 2;
build(data, l, mid, it * 2);
build(data, mid, r, it * 2 + 1);
nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
return;
}
void push(int l, int r, int it) {
int mid = (l + r) / 2;
nodes[it * 2] += tags[it] * (mid - l);
nodes[it * 2 + 1] += tags[it] * (r - mid);
tags[it * 2] += tags[it];
tags[it * 2 + 1] += tags[it];
tags[it] = 0;
return;
}
int query(int queryL, int queryR, int l, int r, int it = 1) {
if (queryL == l && queryR == r) return nodes[it];
push(l, r, it);
int mid = (l + r) / 2;
if (queryR <= mid)
return query(queryL, queryR, l, mid, it * 2);
else if (queryL >= mid)
return query(queryL, queryR, mid, r, it * 2 + 1);
else return query(queryL, mid, l, mid,
it * 2) +
query(mid, queryR, mid, r,
it * 2 + 1);
}
void modify(int modifyL, int modifyR, int delta,
int l, int r, int it = 1) {
if (modifyL == l && modifyR == r) {
nodes[it] += delta * (r - l);
tags[it] += delta;
return;
}
push(l, r, it);
int mid = (l + r) / 2;
if (modifyR <= mid)
modify(modifyL, modifyR, delta, l, mid, it * 2);
else if (modifyL >= mid)
modify(modifyL, modifyR, delta, mid, r, it * 2 + 1);
else {
modify(modifyL, mid, delta, l, mid, it * 2);
modify(mid, modifyR, delta, mid, r, it * 2 + 1);
}
// pull
nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
return;
}
};
# PRESENTING CODE
如果範圍正確
更新此節點值
加上懶標
有點像單點修改+區間查詢
下推
完全在左
完全在右
取得中間值
左右都有
修改目前節點值 (pull)
註: 皆為左閉右開 0-base
變數解釋
\(nodes\) 節點值
\(tags\) 懶標值
\(modifyL\) \(modifyR\) 修改左右區間
\(delta\) 修改增加值
註: \(n\) 代表原資料長度
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
int n;
vector<int> nodes;
SegmentTree()
: n(0) {
nodes.clear();
}
SegmentTree(vector<int> &data)
: n(data.size()) {
nodes.resize(n << 1);
build(data);
}
void build(const vector<int> &data) {
for (int i = n; i--;) nodes[i + n] = data[i];
for (int i = n; i--;)
nodes[i] = nodes[i << 1] + nodes[i << 1 | 1];
return;
}
void pull(int pos) {
for (pos += n; pos >>= 1;)
nodes[pos] = nodes[pos << 1] + nodes[pos << 1 | 1];
return;
}
int query(int l, int r) {
int sum = 0;
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
if (l & 1) sum += nodes[l++];
if (r & 1) sum += nodes[--r];
}
return sum;
}
void modify1(int pos, int modifyValue) {
nodes[pos + n] = modifyValue;
pull(pos);
return;
}
};
# PRESENTING CODE
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
int n;
vector<int> nodes;
SegmentTree()
: n(0) {
nodes.clear();
}
SegmentTree(vector<int> &data)
: n(data.size()) {
nodes.resize(n << 1);
build(data);
}
void build(const vector<int> &data) {
for (int i = n; i--;) nodes[i + n] = data[i];
for (int i = n; i--;)
nodes[i] = nodes[i << 1] + nodes[i << 1 | 1];
return;
}
void pull(int pos) {
for (pos += n; pos >>= 1;)
nodes[pos] = nodes[pos << 1] + nodes[pos << 1 | 1];
return;
}
int query(int l, int r) {
int sum = 0;
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
if (l & 1) sum += nodes[l++];
if (r & 1) sum += nodes[--r];
}
return sum;
}
void modify1(int pos, int modifyValue) {
nodes[pos + n] = modifyValue;
pull(pos);
return;
}
};
# PRESENTING CODE
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
int n;
vector<int> nodes;
SegmentTree()
: n(0) {
nodes.clear();
}
SegmentTree(vector<int> &data)
: n(data.size()) {
nodes.resize(n << 1);
build(data);
}
void build(const vector<int> &data) {
for (int i = n; i--;) nodes[i + n] = data[i];
for (int i = n; i--;)
nodes[i] = nodes[i << 1] + nodes[i << 1 | 1];
return;
}
void pull(int pos) {
for (pos += n; pos >>= 1;)
nodes[pos] = nodes[pos << 1] + nodes[pos << 1 | 1];
return;
}
int query(int l, int r) {
int sum = 0;
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
if (l & 1) sum += nodes[l++];
if (r & 1) sum += nodes[--r];
}
return sum;
}
void modify1(int pos, int modifyValue) {
nodes[pos + n] = modifyValue;
pull(pos);
return;
}
};
# PRESENTING CODE
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
int n;
vector<int> nodes;
SegmentTree()
: n(0) {
nodes.clear();
}
SegmentTree(vector<int> &data)
: n(data.size()) {
nodes.resize(n << 1);
build(data);
}
void build(const vector<int> &data) {
for (int i = n; i--;) nodes[i + n] = data[i];
for (int i = n; i--;)
nodes[i] = nodes[i << 1] + nodes[i << 1 | 1];
return;
}
void pull(int pos) {
for (pos += n; pos >>= 1;)
nodes[pos] = nodes[pos << 1] + nodes[pos << 1 | 1];
return;
}
int query(int l, int r) {
int sum = 0;
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
if (l & 1) sum += nodes[l++];
if (r & 1) sum += nodes[--r];
}
return sum;
}
void modify1(int pos, int modifyValue) {
nodes[pos + n] = modifyValue;
pull(pos);
return;
}
};
# PRESENTING CODE
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
int n;
vector<int> nodes;
vector<int> tags;
SegmentTree()
: n(0) {
nodes.clear();
tags.clear();
}
SegmentTree(vector<int> &data)
: n(data.size()) {
nodes.resize(n << 1, 0);
tags.resize(n, 0);
build(data);
}
void build(const vector<int> &data) {
for (int i = n; i--;) nodes[i + n] = data[i];
for (int i = n; i--;)
nodes[i] = nodes[i << 1] + nodes[i << 1 | 1];
return;
}
void pull(int pos) {
pos += n;
for (int h = 1; pos >>= 1; h++)
nodes[pos] = nodes[pos << 1] + nodes[pos << 1 | 1] +
(tags[pos] << h);
return;
}
void push(int pos) {
pos += n;
for (int h = __lg(pos), it = 1; h--; it = pos >> h) {
if (!tags[it]) continue;
nodes[it << 1] += tags[it] << h;
nodes[it << 1 | 1] += tags[it] << h;
if (h) {
tags[it << 1] += tags[it];
tags[it << 1 | 1] += tags[it];
}
tags[it] = 0;
}
}
int query(int l, int r) {
push(l), push(r - 1);
int sum = 0;
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
if (l & 1) sum += nodes[l++];
if (r & 1) sum += nodes[--r];
}
return sum;
}
void modify(int l, int r, int delta) {
push(l), push(r - 1);
int l2 = l + n, r2 = r + n;
if (l2 & 1) nodes[l2++] += delta;
if (r2 & 1) nodes[--r2] += delta;
for (int h = 1; (l2 >>= 1) < (r2 >>= 1); h++) {
if (l2 & 1) tags[l2] += delta, nodes[l2++] += delta << h;
if (r2 & 1) tags[--r2] += delta, nodes[r2] += delta << h;
}
pull(l), pull(r - 1);
}
};
signed main() {
int n, q;
cin >> n >> q;
vector<int> data(n);
for (int &i : data) cin >> i;
SegmentTree st(data);
for (int mode, l, r, delta; q--;) {
cin >> mode;
if (mode == 1) {
cin >> l >> r >> delta;
st.modify(l - 1, r, delta);
}
else {
cin >> l;
cout << st.query(l - 1, l) << "\n";
}
}
}
# PRESENTING CODE
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct node {
int l, r;
int value = 0, tag = 0;
node *pointerL = nullptr, *pointerR = nullptr;
node(int _l, int _r)
: l(_l), r(_r) {}
node(vector<int> &data) {
l = 0;
r = data.size();
build(data);
}
void build(const vector<int> &data) {
if (r - l == 1) {
value = data[l];
return;
}
int mid = l + r >> 1;
pointerL = new node(l, mid);
pointerR = new node(mid, r);
pointerL->build(data);
pointerR->build(data);
value = pointerL->value + pointerR->value;
return;
}
void push() {
if (r - l == 1) return;
pointerL->value += tag * (pointerL->r - l);
pointerR->value += tag * (r - pointerR->l);
pointerL->tag += tag;
pointerR->tag += tag;
tag = 0;
}
int query(int queryL, int queryR) {
if (queryL == l && queryR == r) return value;
push();
if (queryR <= pointerL->r)
return pointerL->query(queryL, queryR);
else if (queryL >= pointerR->l)
return pointerR->query(queryL, queryR);
else return pointerL->query(queryL, pointerL->r) +
pointerR->query(pointerR->l, queryR);
}
void modify(int modifyL, int modifyR, int delta) {
if (modifyL == l && modifyR == r) {
value += delta * (r - l);
tag += delta;
return;
}
push();
if (modifyR <= pointerL->r)
pointerL->modify(modifyL, modifyR, delta);
else if (modifyL >= pointerR->l)
pointerR->modify(modifyL, modifyR, delta);
else {
pointerL->modify(modifyL, pointerL->r, delta);
pointerR->modify(pointerR->l, modifyR, delta);
}
// pull
value = pointerL->value + pointerR->value;
return;
}
};
signed main() {
int n, q;
cin >> n >> q;
vector<int> data(n);
for (int &i : data) cin >> i;
node st(data);
for (int mode, l, r, u, k; q--;) {
cin >> mode;
if (mode == 1) {
cin >> l >> r >> u;
st.modify(l - 1, r, u);
}
else {
cin >> k;
cout << st.query(k - 1, k) << "\n";
}
}
}
# PRESENTING CODE
根節點
葉節點
分支
根節點的
子節點
根節點
葉節點
分支
根節點的
子節點
至多兩個子節點
根節點
葉節點
根節點的
子節點
分支