LoginSignup
2
1

More than 1 year has passed since last update.

pythonで双方向リスト

Last updated at Posted at 2021-11-17

目的

双方向リストとは何か,Pythonでの実装を通して理解を深めましょう.
この記事で実装した双方向リストは,以下のリポジトリから利用できます.
(TBU)

双方向リストとは

双方向リストの仕様

  • ランダムアクセス不可
  • Nodeが連結してできており,各Nodeは,次のNodeを示すポインタと,前のNodeを示すポインタを持つ
  • Headは先頭の要素を示すポインタ,Tailは末尾の要素を示すポインタである
  • リストの長さを表すLengthプロパティをもつ

doublyLinkedList

双方向リストの各種メソッドの計算量

先頭,末尾の要素に対するInsertion,RemovalはO(1)となります.SinglyLinkedListは,末尾の要素の削除はO(N)でしたから,それにくらべると,計算量小さいです.Searching,AccessはO(N)となります.Technicalには,探すインデックスがLengthの半分より大きいか小さいかで場合を分けるので,計算量はO(N/2)ですが,以前としてO(N)のオーダにとどまります.
双方向リストは,単方向リストに比べ,Previousのポインタを持つことで便利になっています.Searchingは半分の時間で行うことが可能です.しかしながら,Previousポインタをもつことにより,メモリの使用量は増します.

双方向リストの骨組みの実装例

class Node:
    def __init__(self, val):
        val = self.val
        next = None
        prev = None

class DoublyLinkedList:
    def __init__(self):
        head = None
        tail = None
        length = 0

Push

  • 新しいNodeをつくる
  • HeadプロパティがNoneであるならば,HeadとTailを新しく作ったNodeにする
  • そうでないならば,TailのNextプロパティを新しく作ったNodeにする
  • 新しく作ったNodeのPrevプロパティを元のTailにする
  • Lengthをインクリメントする
  • リスト全体を返す
    def push(self,val):
        newNode = Node(val)
        if (not self.head):
            self.head = newNode
            self.tail = self.head
        else:
            self.tail.next = newNode
            newNode.prev = self.tail
            self.tail = newNode

        self.length = self.length + 1
        return self

Pop

  • Headが存在しなければ,Noneを返す
  • のちに返却するため,現在のTailを変数に保存する
  • Lengthが1のとき,HeadとTailをNoneにする
  • TailをひとつまえのNodeにする
  • 新しいTailのNextをNoneにする
  • Lengthをデクリメントする
  • 取り除いたNodeを返す
    def pop(self):
        if(not self.head):
            return None

        currentTail = self.tail
        if (self.length == 1):
            self.tail = None
            self.head = None
        else:
            self.tail = self.tail.prev
            self.tail.next = None
            currentTail.prev = None

        self.length = self.length - 1
        return currentTail

shift

  • lengthが0ならば,Noneをかえす
  • 現在のHeadを変数に保存する(OldHead)
  • Lengthが1のとき,HeadをNoneにし,TailをNoneにする
  • HeadをOldHeadのNextにする
  • Legthをデクリメントする
  • 取り除いたNodeを返す
    def shift(self):
        if (self.length == 0):
            return None
        oldHead = self.head
        if (self.length == 1):
            self.head = None
            self.tail = None
        else:
            self.head = oldHead.next

        self.length = self.length - 1
        return oldHead

unshift

  • 受け取った値でNodeをつくる
  • もし,Lengthが0ならば,Headを新しいNodeにし,Tailも新しいNodeにする
  • HeadのPrevを新しいNodeにする
  • 新しいNodeのNextをHeadにむける
  • Headを新しいNodeにする
  • Lengthをインクリメントする
  • リストを返却する
    def unshift(self, val):
        newNode = Node(val)
        if (self.length == 0):
            self.head = newNode
            self.tail = newNode
        else:
            self.head.prev = newNode
            newNode.next = self.head
            self.head = newNode
        self.length = self.length + 1
        return self

get

  • インデックスが0より小さいか,Lengthと等しいまたはより大きい場合,Noneを返す
  • インデックスがLengthの半分よりも小さいまたは等しい場合,Headからリストを順にたどり,指定されたインデックスのNodeを返す
  • インデックスがLengthの半分よりも大きい場合,Tailからリストを順にたどり,指定されたインデックスのNodeを返す
    def get(self, index):
        if ((index < 0) or (index > self.length)):
            return None

        halfOfLength = self.length // 2
        if (index <= halfOfLength):
            counter = 0
            current = self.head
            while(counter != index):
                current = current.next
                counter = counter + 1
            return current
        elif (index > halfOfLength):
            counter = self.length - 1
            current = self.tail
            while(counter != index):
                current = current.prev
                counter = counter - 1
            return current

set

  • 受け取ったインデックスのGetメソッドの結果を変数に格納する
  • 結果がValidであれば,受け取った値をそのNodeのValueとする
  • Trueをかえす
  • そうでなければFalseを返す
    def set(self, index, val):
        targetNode = self.get(index)
        if(targetNode):
            targetNode.val = val
            return True
        else:
            return False

insert

  • インデックスが0より小さいか,Lengthより大きい場合,Falseを返す
  • インデックスが0のとき,Unshiftする
  • インデックスがLengthと同じ時,Pushする
  • 受け取ったインデックスより1小さいNodeをGetメソッドで得る
  • 受け取った値でNodeをつくる
  • index - 1のNodeのNextを新しいNodeにし,新しいNodeのPrevをindex - 1のNodeにする.index + 1のNodeのPrevを新しいNodeにし,新しいNodeのNextをindex + 1のNodeにする
  • Lengthをインクリメントし,Trueを返す
    def insert(self, index, val):
        if((index < 0) or (index > self.length)):
            return False
        if (index == 0):
            self.unshift(val)
        if (index == self.length):
            self.push(val)
        prevNode = self.get(index - 1)
        newNode = Node(val)
        nextNode = prevNode.next
        prevNode.next = newNode
        newNode.prev = prevNode
        nextNode.prev = newNode
        newNode.next = nextNode
        self.length = self.length + 1

        return True

remove

  • indexが0より小さいか,Lengthより大きいか,Lengthとおなじ時はNoneを返す
  • Indexが0ならば,Shiftする
  • IndexがLength-1と等しいならば,Popする
  • とりのぞくNodeをGetメソッドを使って得る
  • 見つかったNodeのNextをひとつまえのNodeのNextにし,PrevをひとつあとのNodeのPrevにする
  • 見つかったNodeのNextとPrevをNoneにする
  • Lengthをデクリメントする
  • 取り除いたNodeを返す
    def remove(self, index):
        if((index < 0) or (index >= self.length)):
            return None
        if (index == 0):
            self.shift()
        if (index == self.length - 1):
            self.pop()
        removedNode = self.get(index)
        removedNode.prev.next = removedNode.next
        removedNode.next.prev = removedNode.prev
        removedNode.next = None
        removedNode.prev = None

        self.length = self.length - 1
        return removedNode

reverse

  • リストのHeadとTailを入れ替える
  • リストを走査し,各NodeのNextをPrevに,PrevをNextに置き換える
    def reverse(self):
        node = self.head
        self.head = self.tail
        self.tail = node

        tmpPrev = None
        tmpNext = None
        while(node):
            tmpPrev = node.prev
            tmpNext = node.next
            node.next = tmpPrev
            node.prev = tmpNext
            node = node.prev

        return self
2
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
2
1