Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
コンピュータサイエンス第2データ構造
第5回
データ構造
アルゴリズム設計の第⼀歩はデータ構造から▶リスト▶配列▶スタック▶キュー(待ち⾏列)▶⽊構造▶グラフ構造
配列とリスト
配列▶同じ型のデータを決まった個数だけ並べたもの▶記憶装置の連続した番地に要素を並べたもの→ ランダムアクセス:どの要素にも同じ時間でアクセス
’a’ ’b’ ’c’ ’d’ ’e’ ’f’ ’g’ ’h’ ’i’
0 1 2 3 4 5 6 7 8配列 x
x[2]x + 要素サイズ× 2
x[5]x + 要素サイズ× 6
先頭アドレス+要素サイズ×インデックス
配列とリスト
リスト▶配列同様データを並べたもの(同じ型のデータでなければならない場合もある.Pythonは異なっていてよい)▶データを⼊れた箱を参照でつないだもの→ 個数が決まっていない.挿⼊や削除が容易.→ 個々の要素の参照は配列ほど速くない.
リスト x
Pythonのリストは,x[2]やx[5]のように個々の要素にアクセスできるように⾒えるが,前から順にたどっている.
’a’ ’b’ ’c’ ’d’ ’e’
スタックとキュースタック▶データの挿⼊と削除を⼀⽅の端でだけ⾏うという制限を加えたデータの並び(後⼊れ先出し:last-in-first-out,LIFO).▶配列やリストで実現できる(ここではリストを使⽤).→ プッシュ(push):
→ ポップ(pop):
’a’ ’b’ ’c’ ’d’ ’e’
’a’ ’b’ ’c’ ’d’ ’e’ ’f’
ʻfʼをプッシュ
’a’ ’b’ ’c’ ’d’ ’e’ ’f’
’a’ ’b’ ’c’ ’d’ ’e’
ʻfʼをポップ
トップ(top)
スタックとキュースタック▶ Pythonによる実現:リストを利⽤.→ プッシュ:x = []x.append(’a’)x.append(’b’)x.append(’c’)
→ ポップ:x.pop()x.pop()y = x.pop()
スタックとキューキュー(待ち⾏列)▶データを⼀⽅の端から挿⼊,もう⼀⽅の端から取出しを⾏う(先⼊れ先出し:first-in-first-out, FIFO).▶配列やリストで実現できる(ここではリストを使⽤).→ enqueue:
→ dequeue:
’a’ ’b’ ’c’ ’d’ ’e’
’a’ ’b’ ’c’ ’d’ ’e’ ’f’
ʻfʼをenqueue
’a’ ’b’ ’c’ ’d’ ’e’
’a’ ’b’ ’c’ ’d’ ’e’ ’f’ʻfʼをdequeue
tail
head
スタックとキューキュー▶ Pythonによる実現:リストを利⽤.→ enqueue:x = []x.append(’a’)x.append(’b’)x.append(’c’)
→ dequeue:x.pop(0)x.pop(0)y = x.pop(0)
⽊とグラフグラフ構造▶節点(node)の集合と辺(edge)の集合からなる構造▶節点は○で,辺は○と○をつなぐ線あるいは⽮印で表す▶節点uと節点vをつなぐ辺を (u,v) と表す.▶辺に向きがあるグラフを有向グラフ,向きがないグラフを無向グラフという.
→ 有向グラフ:
→ 無向グラフ:
A
C
B
E
D
A
C
B
E
D
⽊とグラフ⽊構造▶グラフの⼀種.閉路がない無向グラフ.▶根となる節点を決めると⽊(根付き⽊)が定まる(有向グラフを⽤いて特定の根付き⽊を表すこともできる).
B C
A
D
E F
H I
G
親(parent)
葉(leaf)
根(root)
⼦(child)
内点(internal node)
祖先(ancestor)
⼦孫(descendant)深さ(depth)根からの辺の数:2
⾼さ(height):3
⽊とグラフ⽊の⾛査(traversal)▶深さ優先探索 ▶幅優先探索
B C
A
D
E F
H I
G
B C
A
D
E F
H I
G
⽊とグラフPython による⽊の実現▶式:( 1 + 2 ) * ( 3 – 4 / 5 ) を表す⽊→ リスト表現
+
*
ー
1 2
4 5
/3
tree = ["*", ["+", 1, 2], ["-", 3, ["/", 4, 5] ]
⽊とグラフ深さ優先探索(depth first search)▶⽊の⾛査:節点を調べる順序→ 前順(pre-order)⾛査:親を調べてから⼦を調べる.
例:* + 1 2 - 3 / 4 5
→ 間順(in-order)⾛査:最初の⼦を調べ,親,残りの⼦を調べる.例:1 + 2 * 3 - 4 / 5
→ 後順(post-order)⾛査:⼦をすべて調べてから親を調べる.例:1 2 + 3 4 5 / - *
def preOrder (t):if type(t) == int:
print(t, end=" ")else:
print(t[0], end=" ")preOrder(t[1])preOrder(t[2])
def inOrder (t):if type(t) == int:
print(t, end=" ")else:
inOrder(t[1])print(t[0], end=" ")inOrder(t[2])
def postOrder (t):if type(t) == int:
print(t, end=" ")else:
postOrder(t[1])postOrder(t[2])print(t[0], end=" ")
⽊とグラフ幅優先探索(breadth first search)▶キューを⽤いた実現例:* + - 1 2 3 / 4 5
def bfs (t):q = []q.append(t)while (q != []):
n = q.pop(0)if type(n) == int:
print(n, end=" ")else:
print(n[0], end=" ")q.append(n[1])q.append(n[2])
⽊とグラフ2分探索⽊(binary search tree)▶各節点にデータがある.▶ある節点nの左側の⼦および⼦孫のデータは,nのデータより⼩さく,右側の⼦孫のデータは⼤きい.→各節点データと⽐較し,左右どちらかを調べればデータが⾒つかる.
8
2
9
15
10
12
25
20 30
例:12の探索
def search (a, key):if a == []:
return []elif a[0] > key:
return search(a[2], key)elif a[0] < key:
return search(a[3], key)else:
return a
⽊とグラフ2分探索⽊(binary search tree)▶挿⼊:探索後たどりついた節点の左右適切な⽅にぶらさげる.→各節点データと⽐較し,左右どちらかを調べればデータが⾒つかる.
8
2
9
15
10
12
25
20 30
例:11の挿⼊
11
⽊とグラフ2分探索⽊(binary search tree)▶挿⼊は,探索後たどりついた節点の左右適切な⽅にぶらさげる.→各節点データと⽐較し,左右どちらかを調べればデータが⾒つかる.
def insert (a, key, binding):if a == []:
return [key, binding, [], []]elif a[0] > key:
return [a[0], a[1], insert(a[2], key, binding), a[3]]elif a[0] < key:
return [a[0], a[1], a[2], insert(a[3], key, binding)]else:
return []
⽊とグラフ2分探索⽊(binary search tree)▶削除:指定のデータを探索し,削除する.削除後,辺をつなぎ合わせるときには,⼤⼩関係を保持.→削除する節点が葉:削除→削除する節点に1つだけ⼦があるとき:削除して,親に⼦をつなぐ.
→削除する節点に2つ⼦があったら? 8
2
9
15
10
12
25
20 30
例:12の削除
11
⽊とグラフ2分探索⽊(binary search tree)▶削除:指定のデータを探索し,削除する.→削除する節点に2つ⼦があるとき:削除する節点を右の最⼩(左の最⼤)の⼦孫で置き換える.
8
2
9
15
10
12
25
20 30
例:8の削除
9
2
15
10
12
25
20 30
⽊とグラフ2分探索⽊(binary search tree)▶ Pythonによる削除の実現
def delete (a, key):if a == []:
return aelif a[0] > key:
return [a[0],a[1],delete(a[2], key),a[3]]elif a[0] < key:
return [a[0],a[1],a[2],delete(a[3], key)]else:
if a[2] == []:return a[3]
elif a[3] == []:return a[2]
else:m = min (a)return [m[0], m[1], delete(a[2],m[0]), a[3]]
def min (a):if a == []:
return aelif a[2] == []:
return aelse:
return min(a[2])
⽊とグラフ⼀般的な(有向)グラフの表現※無向グラフは双⽅向に辺を引いた有向グラフ▶隣接⾏列
▶隣接リスト
21
43
1 1 1 00 1 0 10 1 1 10 0 0 1
1234
1 2 3 4
2 3
2 4
4
⽊とグラフ最短距離問題(Dijkstraのアルゴリズム)▶各節点の開始節からの最短距離を求める▶節点には開始節からの距離,辺には隣接節点間の距離が重み付けしてある.
▶アルゴリズム1. 最短距離が確定した節点の集合をV集合Uの節点には,V内を経由して到達する最短距離を与えておく.
2. U内の節点の中で最⼩の最短距離をもつ節点pを選び,Vに移す.
3. pを始点としてUに属する隣接節点の距離を更新する.
4. 2-3をUが空になるまで繰り返す.
B
AD
C E
F
2
1
3
3
4
6
6
⽊とグラフ最短距離問題(Dijkstraのアルゴリズム)▶データ表現 B
AD
C E
F
2
1
3
3
4
6
6
A = 0B = 1C = 2D = 3E = 4F = 5Infty = 10000
succ = [[B,C], # A[D], # B[E,F], # C[], # D[B,D], # E[] # F
]
edge = [ # A B C D E F [-1, 6, 4,-1,-1,-1], # A[-1,-1,-1, 3,-1,-1], # B [-1,-1,-1,-1, 3, 6], # C[-1,-1,-1,-1,-1,-1], # D[-1, 2,-1, 1,-1,-1], # E[-1,-1,-1,-1,-1,-1] # F
]
dist = [Infty]*len(succ)dist[A] = 0
出発点
⽊とグラフ最短距離問題(Dijkstraのアルゴリズム)▶アルゴリズムの実現 B
AD
C E
F
2
1
3
3
4
6
6
def dijkstra (q):if q == []:
return;else:
u = q.pop()min = Infty_q = []for n in q:
if dist[n] < min:_q.append(u)u = nmin = dist[u]
else:_q.append(n)
for s in succ[u]:if edge[u][s] + dist[u] < dist[s]:
dist[s] = edge[u][s] + dist[u]dijkstra (_q)
dijkstra ([A,B,C,D,E,F])