More Related Content More from Kenta Hattori (20) アルゴリズムとデータ構造83. 2011/4/26 アルゴリズムとデータ構造 8 3
平衡木(バランス木: balanced
tree )
単純な2分探索木では,場合によって形の悪い木が
できてしまうため,最悪の計算量が O(N) となって
しまう.
左右の部分木のバランスがとれた木を用いる
挿入や削除時にバランスが崩れたら,ノードを張り替えて
,調整する.
4. 2011/4/26 アルゴリズムとデータ構造 8 4
AVL 木
左部分木と右部分木の高さの差が,高々1であるような2分探
索木
AVL 木の条件:
全ての内部ノード n について以下を満たす
n が2つの子を持つ場合:
n が1つの子を持つ場合:その子は葉である
例:
これらが AVL 木の条件を満たすことを確認せよ
1).().( ≤− rightnHeightleftnHeight
5. 2011/4/26 アルゴリズムとデータ構造 8 5
AVL 木のノード数と高さの関係
ノード数 N ,高さ h とする
高さ h の木のうち,ノード数が最小のものを考える
h=5 のノード数最小の AVL 木の例:
直観的には,最小のノード数 N は,高さ h の指数関数のオー
ダーで増える
逆に考えると,ノード数 N の AVL 木の高さは高々 O(log N) であ
る
したがって検索に要する最悪の計算量は O(log N) となる
6. 2011/4/26 アルゴリズムとデータ構造 8 6
AVL 木への挿入
通常の2分探索木と同様に新しい頂点を木の葉とし
て,適切な位置に挿入する
その結果,左右の部分木の高さの差が2になれば,
木を再構成して, AVL 木の条件を満たすようにす
る
場合分け:
ケース1:高さが等しかった場合
左右の部分木の高さの差は1なので,再構成する必要なし
ケース2:低い方の部分木に挿入する場合
左右の部分木の高さが等しくなるので再構成する必要なし
ケース3:高い方の部分木に挿入する場合
高さの差が2になるので再構成する必要あり!
7. 2011/4/26 アルゴリズムとデータ構造 8 7
再構成が必要な状態
ケース Left-Left ケース Left-Right
A
B
A
B
0
1
2
h
h+1
h+2
h
h-1 h-1
h-1
h
h-1
βα
γ
βα
γ
右部分木が高いケースはこれと左右対称になる
10. 2011/4/26 アルゴリズムとデータ構造 8 10
2重回転
ケース Left-Right の場合
0
1
2
h
h+1
h+2
左回転
A
B
h-1
h-1 or h-2
h-1
α
γ
3
C
β1 β2
A
α
γ
C
β1
β2
B
右回転
α
B
β1 β2
C
A
γ
11. 2011/4/26 アルゴリズムとデータ構造 8 11
挿入操作の実現
挿入操作:
入力:(部分)木のルート( n ),キー( key ),データ( data )
出力:更新された(部分)木のルート,木の高さが増えたかどうか
def insert(n, key, data):
if n == None:
m = avlnode()
m.key = key; m.data = data; m.left = m.right = None
m.balance = ‘BALANCED’
return m, True
else:
if key < n.key:
n.left, grown = insert(n.left, key, data)
if grown: n, grown = rebalance(n, ‘LEFT’)
elif key > n.key:
n.right, grown = insert(n.right, key, data)
if grown: n, grown = rebalance(n, ‘RIGHT’)
else: error(“key already exists”) # ☆ 2重登録時はエラー
return n, grown
’それぞれ BALANCED’,
‘LEFT’, ‘RIGHT’ とする
木の各ノードに,左右の部
分木の高さが揃っているか
,もしくは左右どちらが高
いかどうかを示すフィール
ドを追加
12. 2011/4/26 アルゴリズムとデータ構造 8 12
挿入操作の実現(2)
木の再構成処理
入力:再構成する部分木のルート( n ),挿入した方向( dir )
出力:再構成された結果更新された部分木のルート,木の高さが増えたかどうか
def rebalance(n, dir):
opp = opposite(dir)
if n.balance == ‘BALANCED’: n.balance = dir; return n, True
elif n.balance == opp: n.balance = ‘BALANCED’; return n, False
n1 = get_child(n, dir)
if n1.balance == dir:
rotate(n, opp)
n.balance = n1.balance = ‘BALANCED’;
return n1, False
elif n1.balance == opp:
n2 = get_child(n1, opp)
rotate(n1, dir); set_child(n, dir, n2); rotate(n, opp)
if n2.balance != dir: n.balance = ‘BALANCED’
else: n.balance = opp
if n2.balance != opp: n1.balance = ‘BALANCED’
else: n1.balance = dir
n2.balance = BALANCED
return n2, False
else: assert False # ありえないケース
13. 2011/4/26 アルゴリズムとデータ構造 8 13
挿入操作の実現(3)
逆方向
def opposite(dir):
if dir == ‘LEFT’: return ‘RIGHT’
elif: dir == ‘RIGHT’: return ‘LEFT’
子ノードの取得
def get_child(n, dir):
if dir == ‘LEFT’: return n.left
elif dir == ‘RIGHT’: return n.right
子ノードのセット
Set_child(n, dir, m):
if dir == ‘LEFT’: n.left = m
elif dir == ‘RIGHT’: n.right = m
回転
def rotate(n, dir):
opp = opposite(dir)
n1 = get_child(n, opp)
set_child(n, opp, get_child(n1,dir))
set_child(n1, dir, n)
14. 2011/4/26 アルゴリズムとデータ構造 8 14
挿入操作の全体的な動作
根から木を下に降りて
いき,一番下の段に挿
入
木をさかのぼりつつ左
右のバランスを回復す
る操作を行う.
再構成の必要が無く
なったら途中で止まる
ケース2,もしくは,
ケース3での回転した場
合 挿入
ケース 1
ケース2,または
ケース3(回転)
15. 2011/4/26 アルゴリズムとデータ構造 8 15
AVL 木からの削除
ノードを削除した結果,左右のバランスが崩
れたら,挿入と同様に木を再構成する
コードは補足資料参照
A
B
A
B
0
1
2
h
h+1
h+2
h
h-1
h-1
h
h-1
α
γ
βα
γ
h
or
h-1
β
16. 2011/4/26 アルゴリズムとデータ構造 8 16
B 木( B-tree )
バランスのとれた m 分木
各ノードに最大 m 個の子供を持つ
AVL 木と同様に,木を再構成することによってバランスを保つ
ただし,回転ではなく,ノードを分割したり,併合したりする
B 木の条件:
各内部ノードの子供の数の最大は m である
根以外の各内部ノードの子供の数の最小は
どの葉も同じ深さである
B 木の例:
2/m
17. 2011/4/26 アルゴリズムとデータ構造 8 17
B 木のノードの構造
各ノードに,隣合う2つの子供の境目を示すキーの
値を入れておく
キーは,左側のデータの最大値より大きく,右側のデータ
の最小値以下の値を入れることとする
ノードが k 個の子供を持つ場合,キーの数は k-1 となる
ここでは,データは葉にだけ入れることにする
6 10 13 20
21 50
30 58 71
3 6 10 14 20 21 39 54 58 77
18. 2011/4/26 アルゴリズムとデータ構造 8 18
B 木の探索
表の探索
入力:探索する B 木 (BT) ,キー (key) ,
出力:見つかったデータ
def search(BT, key):
if BT.root != None:
p = BT.root
while not p.is_leaf:
k = locate(p, key)
p = p.children[k]
if p.key == key: return p.data
retun None
子を選ぶ手続き
入力:内部ノード (p) ,キー (key)
出力:選んだ子のインデックス
def locate(p, key):
i = 0
while i < p.length and key >= p.keys[i]:
i = i + 1
return i
葉か内部ノードか
を区別するフィー
ルド
6 10 13 20
length
ノード内の配列
を線形探索する
19. 2011/4/26 アルゴリズムとデータ構造 8 19
B 木への挿入
新しいデータの挿入位置を決める
葉のノードを作り,親のノードの子供として登録する.
親から見ると,子供が一つ増える
必要に応じて木の再構成を行う
子供の数が M-1 以下なら何もしない
子供の数が M (満杯)ならば,ノードを分割する
例
α β γ δ ε
INSERT
γ δ εα β
20. 2011/4/26 アルゴリズムとデータ構造 8 20
挿入( B 木)の実現
挿入操作:
入力: B 木 (BT) ,キー( key ),データ( data )
def insert(BT, key, data):
if BT.root == None:
leaf = btleaf(); leaf.key = key; leaf.data = data;
BT.root = leaf
return
r = BT.root
if r.is_leaf or r.length == BT.M:
node = btnode(BT.M);
node.length = 1; node.children[0] = r
BT.root = node
if r.length == BT.M: split_child(node, 0, r)
insert_nonfull(node, key, data)
else:
insert_nonfull(r, key, data)
21. 2011/4/26 アルゴリズムとデータ構造 8 21
挿入( B 木)の実現(2)
分割処理:子ノードを分割して,親ノードに追加する
入力:親ノード (x), 子ノードの位置 (i) ,子ノード (y)
def split_child(x, i, y):
z = btnode(BT.M)
z.length = BT.M // 2
off = BT.M – z.length
for j in range(0, z.length-1):
z.keys[j] = y.keys[j+off]
z.children[j] = y.children[j+off]
z.children[j+1] = y.children[j+1+off]
y.length = off
for j in range(x.length, i+1, -1):
x.children[j] = x.children[j-1]
x.children[i+1] = z
for j in range(x.length-1, i, -1):
x.keys[j] = x.keys[j-1]
x.keys[i] = y.keys[off-1]; x.length = x.length + 1
6 10 23 30
11 13 15 17 20 22
x
y
i
22. 2011/4/26 アルゴリズムとデータ構造 8 22
挿入( B 木)の実現(3)
空きがあるノードへの挿入
入力 : ノード (x) ,キー (key) ,データ (data)
def insert_nonfull(x, key, data):
if x.childlen[0].is_leaf:
leaf = btleaf(); leaf.key = key; leaf.data = data
i = x.length - 1
while i > 0 and key < x.keys[i-1]:
x.children[i+1] = x.children[i]; x.keys[i] = x.keys[i-1]; i = i – 1
if x.children[i].key < key:
x.children[i+1] = x.children[i];x.keys[i] = x.children[i].key
else:
x.keys[i] = key; i = i + 1
x.children[i] = leaf
x.length = x.length + 1
else:
i = locate(x, key)
if x.children[i].length == BT.M:
split_child(x, i, x.children[i])
if key > x.keys[i]: i = i + 1
insert_nonfull(x.children[i], key, data)
23. 2011/4/26 アルゴリズムとデータ構造 8 23
B 木からの削除
子供が少なくなりすぎた場合
隣のノードとの間で,子供を再分配する
隣のノードも子の数が最小限のときは,合併する
γ δ εα β ζ η α β γ δ ε ζ η
γ δ εα β α β γ δ ε