見出し画像

データ構造とアルゴリズム | ③スタックとキュー

こんにちは、現役理系大学生のShogoです!

第3回の内容は、「スタックとキュー」です。

アルゴリズムを勉強する上で、一度は耳にしたことのある言葉ではないでしょうか。実際この形式は、コンピュータ上でよく使われています。例えば、webページのブラウザバックをする際に用いられています。

今回は、そんな「スタックとキュー」を詳しく説明していきます!


6-1 スタックとキューとは

画像8

スタック(stack)とは、積み重ねるという意味です。

例として、輪投げを想像してください。輪投げは、的にいれたら輪が積み重なっていきますよね。的から輪をなくしたい場合は、上から1つずつとりだします。

このようにスタックは、後に入れたものが先に取り出されます。これを「LIFO(Last In First Out)」といいます。


キュー(queue)とは、待ち行列という意味です。このときの行列という意味は、数学的な行列ではなく、コンビニやラーメン屋で並ぶという意味での行列です。

例として、ダルマ落としを想像してください。あたりまえですが、ダルマ落としは下から積んでいきますよね。そして、胴体は一番下から飛ばします。

このようにキューは、先に入れたものが先に取り出されます。これを「FIFO(First In First Out)」といいます。


✅共通した操作

スタックとキューに共通した操作は、追加する操作と取り出す操作です。

前者は、要素をデータ構造に追加するという操作です。輪投げでいうと輪を的に入れる、ダルマ落としでいうと積み木を積むという操作です。

対して後者は、要素をデータ構造から取り出すという操作です。輪投げでいうと輪を取り出す、ダルマ落としでいうと積み木を叩くという操作です。


6-2 スタックをコードで解説

先程例であげた輪投げ(quoits)を用いて、スタックをコードで説明していきます。

✅的を用意する

画像2

class Quoits(object):
   def __init__(self):
       self.stack = [] # 的を用意する

quoits = Quoits()
print(quoits.stack)
# 出力結果
>> []

「quoits = Quoits()」で初期値である「__init__」が呼び出されます。したがって、輪投げの的となる「[]」が出力されます。


✅輪を投げる

画像1

続いて、輪をひとつずつ投げ、的となる「[]」に入れていきます。

class Quoits(object):
   def __init__(self):
       self.stack = []
       
   def push(self, ring):
       self.stack.append(ring) # 輪を的に入れる

# 的を用意する
quoits = Quoits()
print(quoits.stack)

# 輪を的に入れる 
quoits.push(0)
print(quoits.stack)
quoits.push(1)
print(quoits.stack)
quoits.push(2)
print(quoits.stack)
# 出力結果
>> []
>> [0]
>> [0, 1]
>> [0, 1, 2]

要素を追加する場合は、「quoits.push()」を使います。「quoits.push()」の「()」の値を変えることで、ひとつずつ的に値を入れていけます。


✅輪をひとつずつ片付ける

画像3

最後に、輪をひとつずつ片付けます。

class Quoits(object):
   def __init__(self):
       self.stack = []
       
   def push(self, ring):
       self.stack.append(ring)
       
   def pop(self):
       if self.stack:
           return self.stack.pop() # 輪をひとつずつ片付ける

# 的を用意する
quoits = Quoits()

# 輪を的に入れる
quoits.push(0)
quoits.push(1)
quoits.push(2)
print(quoits.stack)

# 輪をひとつずつ片付ける
quoits.pop()
print(quoits.stack)
quoits.pop()
print(quoits.stack)
quoits.pop()
print(quoits.stack)
# 出力結果
>> [0, 1, 2]
>> [0, 1]
>> [0]
>> []

要素を取り出す場合は、「quoits.pop()」を使います。「quoits.pop()」を記述することで、ひとつずつ要素を取り出すことができます。


✅「__init__」?「def」?「class」?

もしここででた「__init__」や「def」、「class」がわからない方は、こちらの記事を見てください!


6-3 キューをコードで解説

同様に、先程例であげたダルマ落とし(daruma otoshi / daruma doll drop)を用いて、キューをコードで説明していきます。

✅安定した土台を用意する

画像5

class DarumaOtoshi(object):
   def __init__(self):
       self.queue = []
       
# 安定した土台を用意する
darumaotoshi = DarumaOtoshi()
print(darumaotoshi.queue)
# 出力結果
>> []

「darumaotoshi = DarumaOtoshi()」で初期値である「__init__」が呼び出されます。したがって、安定した土台である「[]」が出力されます。


✅ダルマの胴体を積む

画像6

class DarumaOtoshi(object):
   def __init__(self):
       self.queue = []
       
   def enqueue(self, block):
       self.queue.append(block)

# 安定した土台を用意する
darumaotoshi = DarumaOtoshi()
print(darumaotoshi.queue)

# ダルマの胴体を積む
darumaotoshi.enqueue(0)
print(darumaotoshi.queue)
darumaotoshi.enqueue(1)
print(darumaotoshi.queue)
darumaotoshi.enqueue(2)
print(darumaotoshi.queue)
# 出力結果
>> []
>> [0]
>> [0, 1]
>> [0, 1, 2]

スタックとやることは一緒ですが、要素を追加する場合は、「darumaotoshi.enqueue()」を使います。「darumaotoshi.enqueue()」の「()」の値によって、ひとつずつ木を積んでいます。


✅積み木を弾く

画像7

class DarumaOtoshi(object):
   def __init__(self):
       self.queue = []

   def enqueue(self, block):
       self.queue.append(block)

   def dequeue(self):
       if len(self.queue) == 0:
           return None
       result = self.queue[0]
       del self.queue[0]
       return result

# 安定した土台を用意する
darumaotoshi = DarumaOtoshi()

# ダルマの胴体を積む
darumaotoshi.enqueue(0)
darumaotoshi.enqueue(1)
darumaotoshi.enqueue(2)
print(darumaotoshi.queue)

# 積み木を弾く
darumaotoshi.dequeue()
print(darumaotoshi.queue)
darumaotoshi.dequeue()
print(darumaotoshi.queue)
darumaotoshi.dequeue()
print(darumaotoshi.queue)
darumaotoshi.dequeue()
# 出力結果
>> [0, 1, 2]
>> [1, 2]
>> [2]
>> []

要素を取り出す場合は、「darumaotoshi.dequeue()」を使います。この点は、スタックと異なるところです。0→1→2と順番に積んだ木を0から順に弾いていきます。


6-4 ライブラリで簡略化

今挙げたスタックとキューは、わざわざ長いコードを書かなくても実行することができます。

from collections import deque

# スタック
s = deque()

# 要素の追加
s.append(0)
s.append(1)
s.append(2)
print(s)

# 要素の取り出し
s.pop()
print(s)
s.pop()
print(s)
s.pop()
print(s)
# 出力結果
>> deque([0, 1, 2])
>> deque([0, 1])
>> deque([0])
>> deque([])

from collections import deque」でライブラリを使うことで、スタックを実行できます。

また、使っている関数は違いますが、キューも同じように簡略化できます。

from collections import deque

# キュー
q = deque()

# 要素の追加
q.append(0)
q.append(1)
q.append(2)
print(q)

# 要素の取り出し
q.popleft()
print(q)
q.popleft()
print(q)
q.popleft()
print(q)
# 出力結果
>> deque([0, 1, 2])
>> deque([1, 2])
>> deque([2])
>> deque([])


まとめ

画像4

いかがだったでしょうか。

今回は、「スタックとキュー」についてお話しました。

スタックとキューは、大学の授業や情報系の試験で頻出する内容なので、ぜひここで理解しておいてください!


質問や訂正があれば、遠慮なくこの記事にコメント、もしくは、TwitterにDMをしてください!(@shogo_python)

今回の記事が"いいね!"と思ってくれた方は、スキとフォローをよろしくおねがいします!

では、またの機会にお会いしましょう!


データ構造とアルゴリズム 入門講座 一覧はこちらから


<参考文献>
「スタックとキュー パソコン初心者講座」〈https://www.pc-master.jp/words/stack-queue.html〉