Braun Tree
基本定义
这里仅给出Binary
Braun Tree定义(多叉树理论上可以此类推,emm):
一个二叉树是Braun Tree当且仅当它满足以下性质
- 它是空树
- 或者它的左子树和右子树均为Braun Tree
- 左右子树节点数 N l , N r N_l,N_r Nl,Nr(简记为 ∣ L ∣ , ∣ R ∣ |L|,|R| ∣L∣,∣R∣)满足 ∣ R ∣ ≤ ∣ L ∣ ≤ ∣ R ∣ + 1 |R|\le |L|\le |R|+1 ∣R∣≤∣L∣≤∣R∣+1
其中第三点对于Braun良好的平衡性以及操作的简洁高效有着至关重要的意义,且下面的操作几乎均基于这一性质有关,是理解Braun Tree的关键。
braun tree实现的弹性数组(flexiable array based on Braun Tree)
braun tree本身对节点顺序(序号)并无要求,但是我们用如下形式给braun tree的节点编号,可以很好地组织braun tree。
数据类型
使用最简单的树
结构
datatype a' tree = Lf
| Br of a'*a' tree*a' tree
基本操作(及理解)
- sub
fun sub(Lf, _) = raise Subsrcipt
| sub(Br(v, tl, tr), k) =
if k = 1 then v
else if k mod 2 = 0
then sub(tl, k div 2)
else sub(tr, k div 2)
理解:按上图,它的组织方式类似哈夫曼编码,第h层的序号均为长h(前缀0不计入长度,或者说首位必为1)的二进制串。对于 k = ( a h a h − 1 … a 1 a 0 ) 2 k=(a_ha_{h-1}\ldots a_1a_0)_2 k=(ahah−1…a1a0)2,从低位到高位按照 a i a_i ai的值决定路径, a i = 0 a_i=0 ai=0(即 k m o d 2 = 0 k \mod 2 = 0 kmod2=0)向左子树走, a i = 1 ( k m o d 2 = 1 ) a_i=1(k\mod 2 =1 ) ai=1(kmod2=1)向右子树走。
- update
- delete
- loext(从根处扩展数组)
fun loext(Lf, w) = Br(w, Lf, Lf)
| loext(Br(v, tl, tr), w) =
Br(w, loext(tr, v), tl)
理解:
low extend
从低处扩展,即从根部插入节点扩展弹性数组- 直观描述算法
原根节点 1 → 2 1\rightarrow 2 1→2,应放入2号位置;原左子树 ( 2 , 4 , 6 , … ) (2,4,6,\ldots) (2,4,6,…)序号 + 1 +1 +1后变为 ( 3 , 5 , 6 , … ) (3,5,6,\ldots) (3,5,6,…),直接变为扩展后的右子树;扩展后左子树显然变为新的相同的子问题,即将原根节点 ( 1 → 2 ) (1\rightarrow 2) (1→2)扩展到原右子树上。
(为什么说子问题是相同的?注意到原每个子树都是braun tree,那么原右子树一定是braun tree)
- 保Braun tree性(即扩展后的树仍然为Braun Tree)
既然从低处扩展数组,那么应该将根节点下沉,由于Braun Tree的性质要求 ∣ R ∣ ≤ ∣ L ∣ ≤ ∣ R ∣ + 1 |R|\leq |L|\le |R|+1 ∣R∣≤∣L∣≤∣R∣+1,将根节点沉入右子树,此时满足
∣ L ∣ ≤ ∣ R ′ ∣ ≤ ∣ L ∣ + 1 ( ∣ R ′ ∣ = ∣ R ∣ + 1 ) |L|\le |R'|\le|L|+1(|R'|=|R|+1) ∣L∣≤∣R′∣≤∣L∣+1(∣R′∣=∣R∣+1) high extend
从高处扩展Braun Tree,其实和loext几乎完全相同,唯一的区别就是下沉的不是原节点而是插入节点
fun hiext(Lf, w) = Br(w, Lf, Lf)
| hiext(Br(v, tl, tr), w) =
Br(v, hiext(tr, w), tl)
- lorem
理解:loext 的逆过程,形象地描述为“上浮”过程,不再赘述;hirem 同理。 - length(count函数)
一般来说,计算树的节点个数只需把每个节点遍历一遍即可,花费 O ( n ) O(n) O(n)时间。但在 B r a u n Braun Braun T r e e Tree Tree中却可以达到 O ( l o g n ) O(logn) O(logn)。
fun diff Br(_, Lf, Lf) 0 = 0
| diff Br(_, Lf, Lf) 1 = 1
| diff Br(_, tl, tr) n =
if n mod 2 = 1 then diff tl (n div 2)
else diff tr (n div 2)
fun length Lf = 0
| length Br(_, Lf, Lf) = 1
| length Br(_, tl, tr) =
let val nl = length tr
in 1+2*nl+(diff tl nl) end
理解:
由Braun Tree重要性质
:
∣
R
∣
≤
∣
L
∣
≤
∣
R
∣
+
1
→
∣
L
∣
=
∣
R
∣
or
∣
R
∣
+
1
|R|\le|L|\le|R|+1\rightarrow |L|=|R|\text{ or }|R|+1
∣R∣≤∣L∣≤∣R∣+1→∣L∣=∣R∣ or ∣R∣+1
只需计算其中一个子树的节点数
m
m
m然后总节点数最多等于
1
+
2
∗
m
1+2*m
1+2∗m再加上1,这由
∣
L
∣
|L|
∣L∣是等于
∣
R
∣
|R|
∣R∣还是
∣
R
∣
+
1
|R|+1
∣R∣+1决定,于是我们定义一个函数diff
,它返回1当
∣
L
∣
=
∣
R
∣
|L|=|R|
∣L∣=∣R∣,否则返回0。
这样可以得到树t的节点个数为
length t as Br(r, tl, tr) = 1 + 2*m + (diff tl m)
下面给出diff
的递推式:
假设
t
=
B
r
(
r
,
t
l
,
t
r
)
,
t
l
=
B
r
(
r
1
,
l
1
,
r
1
)
,
t
r
t=Br(r,t_l,t_r),t_l=Br(r_1,l_1,r_1),t_r
t=Br(r,tl,tr),tl=Br(r1,l1,r1),tr
其中
∣
t
r
∣
=
m
|t_r|=m
∣tr∣=m,再根据Braun Tree性质
:
∣
R
∣
≤
∣
L
∣
≤
∣
R
∣
+
1
|R|\le|L|\le|R|+1
∣R∣≤∣L∣≤∣R∣+1
可知
∣
t
l
∣
=
m
或
m
+
1
|t_l|=m\text{ 或 }m+1
∣tl∣=m 或 m+1,对
m
m
m分奇偶讨论:
- m = 2 k + 1 m=2k+1 m=2k+1时,如上图根 r 1 r_1 r1的树,无论 ∣ t l ∣ = m 还 是 m + 1 |t_l|=m还是m+1 ∣tl∣=m还是m+1,其右子树节点个数 ∣ r 1 ∣ |r_1| ∣r1∣均为 k k k,于是可知,要判断是否有 ∣ t l ∣ = m |t_l|=m ∣tl∣=m,关键是判断是否有 ∣ l 1 ∣ = k |l_1|=k ∣l1∣=k,代码表示为
diff tl 2k+1 = diff l1 k
- m = 2 k + 2 m=2k+2 m=2k+2时,如上图根 r 2 r_2 r2的树,分析过程几乎奇数时相同,此时 t t t的左子树的节点个数 ∣ l 1 ∣ |l_1| ∣l1∣均为 k + 1 k+1 k+1,判断依据用代码表示
diff tl 2k+2 = diff r1 k+1