LoginSignup
0
1

More than 1 year has passed since last update.

「新・明解Pythonで学ぶアルゴリズムとデータ構造」で勉強日記#17

Posted at

【出典】「新・明解Pythonで学ぶアルゴリズムとデータ構造」
前回の記事はこちら

4-2 キュー

キューはデータ構造の一つで、先入先出の機構です。(レジの行列のイメージ)
キューに追加する操作をエンキュー、取り出す操作をデキューと呼び、取り出される側を先頭、データが押し込まれる側を末尾と呼びます。

コラム4-5 優先度付キュー

優先度付のキューを追加するとデキューする際に優先度順に取り出すことができる。
heapqモジュールで提供されており、heapq.heappsh(heap, data)でキューを、heap.heappop(heap)でデキューをします。(6章でまた扱います)

リングバッファーによるキューの実現

デキューの際に配列内の要素をずらすことなくキューを実現するために用いるのがリングバッファーです。(切られたピザのようなイメージ)
先頭と末尾はfrontとrearで管理します。
リングバッファーを使うことで、要素の移動が不要になり、計算量はO(1)となります。

list4-3
#固定長キュークラス

from typing import Any

class FixedQueue:

    class Empty(Exception):
        """空のFixedQueueに対してdequeあるいはpeekが呼び出されたときに送出する例外"""

    class Full(Exception):
        """満杯のFixedQueueに対してenpueが呼び出されたときに送出する例外"""
        pass

    def __init__(self, capacity: int) -> None:
        """初期化"""
        self.no = 0 #現在のデータ数
        self.front = 0 #先頭要素カーソル
        self.rear = 0 #末尾要素カーソル
        self.capacity = capacity #キューの容量
        self.que = [None] * capacity #キューの本体

    def __len__(self) -> int:
        """キューに押し込まれているデータ数を返す"""
        return self.no

    def is_empty(self) -> bool:
        """キューは空であるか"""
        return self.no <= 0

    def is_full(self) -> bool:
        """キューは満杯か"""
        return self.no >= self.capacity


    def enque(self, x: Any) -> None:
        """データxをエンキュー"""
        if self.is_full():
            raise FixedQueue.Full
        self.que[self.rear] = x
        self.rear += 1
        self.no += 1
        if self.rear == self.capacity:
            self.rear = 0


    def deque(self) -> Any:
        """データをデキュー"""
        if self.is_empty():
            raise FixedQueue.Empty
        x = self.que[self.front]
        self.front += 1
        if self.front == self.capacity:
            self.front = 0
        return x


    def peek(self) -> Any:
        """データをピーク(先頭データをのぞく)"""
        if self.is_empty():
            raise FixedQueue.Empty #キューは空
        return self.que[self.front]

    def find(self, value: Any) -> Any:
        """キューからvalueを探して添字(見つからなければ-1)を返す"""
        for i in range(self.no):
            idx = (i + self.front) % self.capacity
            if self.que[idx] == value: #探索成功
                return idx
        return -1 #探索失敗

    def count(self, value: Any) -> bool:
        """キューに含まれるvalueの個数を返す"""
        c = 0
        for i in range(self.no): #底側から線形探索
            idx = (i + self.front) % self.capacity
            if self.que[idx] == value:
                c += 1
        return c

    def __contains__(self, value: Any) -> bool:
        """キューにvalueは含まれているか"""
        return self.count(value)

    def clear(self) -> None:
        """キューを空にする"""
        self.no = self.front = self.rear = 0

    def dump(self) -> None:
        """全データを先頭→末尾の順に表示"""
        if self.is_empty():
            print('キューは空です')
        else:
            for i in range(self.no):
                print(self.que[(i + self.front) % self.capacity], end=' ')
            print()
list4-4
#固定長スタックStackの利用例

from enum import Enum
#from fixed_queue import FixedQueue

Menu = Enum('Menu', ['エンキュー', 'デキュー', 'ピーク', '探索', 'ダンプ', '終了'])

def select_menu() -> Menu:
    """メニュー選択"""
    s = [f'({m.value}){m.name}' for m in Menu]
    while True:
        print(*s, sep=' ', end='' )
        n = int(input(':'))
        if 1 <= n <= len(Menu):
            return Menu(n)

q = FixedQueue(64) #最大64個プッシュできるスタック

while True:
    print(f'現在のデータ数:{len(q)} / {q.capacity}')
    menu = select_menu() #メニュー選択

    if menu ==  Menu.エンキュー: #エンキュー
        x = int(input('データ:'))
        try:
            q.enque(x)
        except FixedQueue.Full:
            print('キューが満杯です。')

    elif menu == Menu.デキュー: #デキュー
        try:
            x = q.deque()
            print(f'デキューしたデータは{x}です。')
        except  FixedQueue.Empty:
            print('キューが空です。')

    elif menu == Menu.ピーク: #ピーク
        try:
            x = q.peek()
            print(f'ピークしたデータは{x}です。')
        except FixedQueue.Empty:
            print('キューが空です。')

    elif menu == Menu.探索: #探索
        x = int(input('値:')) 
        if x in q:
            print(f'{q.count(x)}個含まれ先頭の位置は{q.find(x)}です。')
        else:
            print('その値は含まれません。')

    elif menu == Menu.ダンプ:
            q.dump()

    else:
        break
コラム4-6 両方向待ち行列(デック)

デックと呼ばれる両方向待ち行列は、先頭末尾両方からデータの押し込み・取出しが行えるデータ構造で、collections.dequeとして提供されています。

コラム4-7 リングバッファの応用例
list4C-2

#好きな個数だけ値を読み込んで要素数nの配列に最後のn個を格納

n = int(input('何個の整数を記憶しますか:'))
a = [None] * n #読み込んだ値を格納する配列

cnt = 0
while True:
    a[cnt % n] = int(input((f'{cnt + 1}個目の整数:')))
    cnt += 1

    retry = input(f'続けますか?(Y…Yes / N…No) :')
    if retry in {'N', 'n'}:
        break

i = cnt - n
if i < 0: i = 0

while i < cnt:
    print(f'{i + 1}個目={a[i % n]}')
    i += 1

以上です。今回で4章が終了です。
ありがとうございました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1