More Related Content
More from Kenta Hattori (20)
アルゴリズムとデータ構造4
- 3. 2011/4/19 アルゴリズムとデータ構造 4 3
スタック( Stack )
データの挿入や削除を一方
の端だけで行うように制限
を加えた列
後から入れた新しいデータ
が先に取り出されるので,
この性質を後入れ先出し
( LIFO : Last In First
Out )と呼ぶ.
スタック S への操作
スタックへ要素 x の挿入
push(S, x)
スタックから値を取り出す
pop(S)
スタックの初期化
stack_init(S)
・・・
push pop
スタックの途中の
要素を参照したり
,挿入,削除した
りすることはでき
ない
- 4. 2011/4/19 アルゴリズムとデータ構造 4 4
抽象データ型
インタフェース(つまりデータに対する可能な操
作)によって定義されるデータ型
そのデータが実際にどのように実装(アルゴリズム
やデータ構造)されるか隠蔽される
なぜうれしいか?
実装を(後から)変更しても,利用者は影響を受けないで
済む
変更箇所を局所的に閉じ込めておくことができる
ちなみに抽象データ型の概念を発展させるとオブ
ジェクト指向にいきつく
- 5. 2011/4/19 アルゴリズムとデータ構造 4 5
配列によるスタックの実現
スタックの一番上の位置を示すインデックス sp
(スタックポインタ)と,配列を使う
データの挿入,削除に応じて, sp を増減させる
スタックの初期化
スタック S を大きさ size で初期化する
def stack_array_init(S, size):
S.sp = 0
S.elems = array(size)
・・・
S.elems
S.elems[0]
[1]
[2]
[3]
[size-1]
S.sp
- 6. 2011/4/19 アルゴリズムとデータ構造 4 6
配列によるスタックの実現(2)
スタックへの挿入操作( push )
def stack_array_push(S, x):
if S.sp >= len(S.elems):
error(“stack overflow”)
S.elems[S.sp] = x
S.sp = S.sp + 1
スタックからの取り出し操作( pop )
def stack_array_pop(S):
if S.sp <= 0:
error(“stack underflow”)
S.sp = S.sp – 1
val = S.elems[S.sp]
return val
配列のサイズを
超える数の要素
は挿入できない
空のスタックから
要素を取り出そう
とするとエラーと
なる
- 7. 2011/4/19 アルゴリズムとデータ構造 4 7
リストによるスタックの実現
リストの先頭についてのみ要素を出し入れする
スタックの初期化
def stack_list_init(S):
S.top = None
最大サイズを指定する必要が無いことに注意
25 9 16 4 1
S.top nextelem
9 16 4 1
push(S, 25)
- 8. 2011/4/19 アルゴリズムとデータ構造 4 8
リストによるスタックの実現
(2)
スタックへの挿入操作( push )
def stack_list_push(S, x):
n = node()
n.elem = x
n.next = S.top
S.top = n
スタックからの取り出し操作( pop )
def stack_list_pop(S):
if S.top == None: error(“stack underflow”)
val = S.top.elem
S.top = S.top.next
return val
リストの先頭に要
素を挿入する操作
配列による実現と異な
り,(メモリのある限
り)いくらでも要素を
挿入できる
- 9. 2011/4/19 アルゴリズムとデータ構造 4 9
練習問題
配列版とリスト版のそれぞれに対して,ス
タックが空かどうかを判定する関数(手続
き) stack_array/list_isempty(S) を書け
スタックが空なら True を返し,そうでなければ
, False を返す
stack_array/list_init(S) の直後
に, stack_array/list_isempty(S) を呼び出せ
ば,必ず True が返ることを確認せよ
- 10. 2011/4/19 アルゴリズムとデータ構造 4 10
解答例
配列版
def stack_array_isempty(S):
return S.sp == 0
リスト版
def stack_list_isempty(S):
return S.top == None
- 11. 2011/4/19 アルゴリズムとデータ構造 4 11
キュー(待ち行列)
データの挿入を一方の端で行い,取り出しをもう一
方の端から行うように制限を加えた列
先に入れた古いデータから先に取り出されるので,この性
質を先入れ先出し( FIFO : Fast In First Out )と呼ぶ.
キュー Q への操作
キューへ要素 x の挿入
enqueue(Q, x)
キューから値を取り出す
dequeue(Q)
キューの初期化
queue_init(Q)
・・・dequeue enqueue
- 12. 2011/4/19 アルゴリズムとデータ構造 4 12
リストによるキューの実現
キューの先頭のノードを Q.head ,末尾のノードを
Q.tail で指すようにする
キューの初期化
def queue_list_init(Q):
Q.head = None
キューが空かどうかの判定
def queue_list_isempty(Q):
return Q.head == None
nextelem
9 16 4 1
Q.head Q.tail
Q.head
?
Q.tail
キューの末尾の
値は不定でもよ
い
- 13. 2011/4/19 アルゴリズムとデータ構造 4 13
リストによるキューの実現(2)
キューへの挿入操作( enqueue )
キューの末尾へ要素を追加する
def queue_list_enqueue(Q, x):
n = node()
n.elem = x
n.next = None
if Q.head == None:
Q.head = n
else:
Q.tail.next = n
Q.tail = n
nextelem
9 16 4 1
Q.head Q.tail
9 16 4 1 25
enqueue(Q,25)
- 14. 2011/4/19 アルゴリズムとデータ構造 4 14
リストによるキューの実現(3)
キューからの取り出し操作( dequeue )
キューの先頭から取り出していく
def queue_list_dequeue(Q):
if Q.head == None: error(“queue underflow”)
val = Q.head.elem
Q.head = Q.head.next
return val
9 16 4 1 25
Q.head Q.tail
16 4 1 25
実はスタック(リス
ト版)の取り出し操
作と同じ
dequeue(Q)
- 15. 2011/4/19 アルゴリズムとデータ構造 4 15
練習問題
番兵付きリストを用いてキューを以下のように実現することを
考える
def queue_list_with_sentinel_init(Q):
n = node()
n.next = None
Q.head = n
Q.tail = n
isempty(Q) と
enqueue(Q, x) と
dequeue(Q) を書け nextelem
16 4 1
Q.head Q.tail
Q.head Q.tail
ダミーノード(番
兵)
- 17. 2011/4/19 アルゴリズムとデータ構造 4 17
配列によるキューの実現
問題点
スタックと違って,出し入れを繰り返していると,いずれ
配列の端からデータがはみ出してしまう.
解決策1
はみ出しそうになったら,左側につめなおす.
O(N) の時間がかかってしまう
enqueue と dequeue を繰り返す
Q.head Q.tail
Q.head Q.tail
まだ空きがあるの
に,挿入できな
い!
- 18. 2011/4/19 アルゴリズムとデータ構造 4 18
配列によるキューの実現(2)
解決策2
右端に到達したら,次のデータは左端に入れる
リングバッファ( ring buffer )と呼ぶ
Q.tail
Q.head Q.tail
Q.headQ.tail
- 19. 2011/4/19 アルゴリズムとデータ構造 4 19
リングバッファによるキューの実
現
キューの初期化
def queue_rbuf_init(Q, sz):
Q.count = 0
Q.head = Q.tail = 0
Q.elems = array(sz) # 最大サイズ sz の配列
生成
キューが空かどうかの判定
def queue_rbuf_isempty(Q):
return Q.count == 0
キューが満杯かどうかの判定
def queue_rbuf_isfull(Q):
return Q.count == len(Q.elems)
- 20. 2011/4/19 アルゴリズムとデータ構造 4 20
リングバッファによるキューの実現
(2)
キューへの挿入( enqueue )
def queue_rbuf_enqueue (Q, x):
if Q.count >= len(Q.elems): error(“queue overflow”)
Q.elems[Q.tail] = x
Q.tail = Q.tail + 1
if Q.tail >= len(Q.elems): Q.tail = 0
Q.count = Q.count + 1
キューからの取り出し( dequeue )
def queue_rbuf_dequeue(Q):
if Q.count <= 0: error(“queue underflow”)
val = Q.elems[Q.head]
Q.head = Q.head + 1
if Q.head >= len(Q.elems): Q.head = 0
Q.count = Q.count – 1
return val
- 21. 2011/4/19 アルゴリズムとデータ構造 4 21
練習問題
さきほどの実現方法では,キューが空のときも満杯のときも,
Q.head = Q.tail となる.
このため,この 2 つの状態を区別するために,フィールド count
を導入した
以下のように count を使わない方法も考えられる.
def queue_rbuf2_init(Q, sz):
Q.head = Q.tail = 0
Q.elems = array(sz + 1)
def queue_rbuf2_isempty(Q):
return Q.head == Q.tail
def queue_rbuf2_isfull(Q):
return ((len(Q.elems) + Q.head – Q.tail) %
len(Q.elems)) == 1
enqueue (Q, x) と dequeue(Q) を書け