両方向連結リスト
前回のブログ投稿で片方向連結リストの実装について見てきましたね。今回の投稿では、その兄弟版の両方向連結リストについて詳しく見ていきましょう。
両方向連結リストを理解する
片方向連結リストと同じく、両方向連結リストとはノードの集まりから構成されるデータ構造です。この場合の各ノードは個別のオブジェクトですが、ユニークな点は各ノードが次のノードにだけでなく前のノードにもリンクされていることです。この双方向のリンクにより、両方向への効率的なトラバース(探索)が可能になります。
片方向連結リストが次の要素を指し示すのに対し、両方向連結リストでは各ノードが二つのポインタを持ちます。一つは次の要素を指し、もう一つは前の要素を指します。
ノードの追加
両方向連結リストにノードを追加するのは、片方向連結リストより少し複雑です。prev
ノードとnext
ノードの間にcur
ノードを挿入するとしましょう。これを実現するためには次の手順を踏む必要があります:
prev
ノードの参照フィールドをcur
ノードを指すように設定します。cur
ノードの参照フィールドをnext
ノードを指すように設定します。- 双方向リンクを完成させるために、
next
ノードの参照フィールドもcur
ノードを指し戻すように調整します。
両方向連結リストの挿入プロセスも効率が保たれ、prev
とnext
ノードへの参照があればO(1)の時間計算量で新しいノードを挿入できます。
ノードの削除
両方向連結リストでのノード削除も、挿入と同様に、双方向リンクのため少し手間がかかります。削除したいノードcur
について考えましょう:
- 先に
cur
ノードの前のノードprev
と次のノードnext
を探します。 cur
ノードからのリンクを断ち切るために、prev
ノードの参照フィールドをnext
ノードを指すように調整します。prev
ノードを指し戻すためにnext
ノードの参照フィールドも調整します。
両方向連結リストでは、参照フィールドを使用して次のノードnext
を簡単に見つけることができます。しかし、前のノードprev
を見つけるのも同様に効率的です。なぜならリストを両方向にトラバースできるからです。ノードを削除する時間計算量もO(1)です。
Pythonを使用した実装
それでは、Pythonでの両方向連結リストの実装を見ていきましょう:
# ListNodeクラスは両方向連結リストのノードを表します。
class ListNode:
def __init__(self, val):
self.val = val # 現在のノードの値
self.prev = None # 前のノードへの参照
self.next = None # 次のノードへの参照
# MyLinkedListクラスは指定されたメソッドで両方向連結リストを実装します。
class MyLinkedList:
def __init__(self):
self.head = None # 先頭ノード(最初のノード)への参照
self.tail = None # 末尾ノード(最後のノード)への参照
self.size = 0 # 連結リストのノード数
# 連結リストの指定されたインデックスにあるノードの値を取得します。
def get(self, index):
if index < 0 or index >= self.size:
return -1 # 無効なインデックスには-1を返します
current = self.head
for _ in range(index):
current = current.next
return current.val
# 指定された値で新しいノードを連結リストの先頭に追加します。
def addAtHead(self, val):
new_node = ListNode(val)
if self.size == 0:
self.head = self.tail = new_node
else:
new_node.next = self.head
self.head.prev = new_node
self.head = new_node
self.size += 1
# 指定された値で新しいノードを連結リストの末尾に追加します。
def addAtTail(self, val):
new_node = ListNode(val)
if self.size == 0:
self.head = self.tail = new_node
else:
new_node.prev = self.tail
self.tail.next = new_node
self.tail = new_node
self.size += 1
# 指定されたインデックスのノードの前に、与えられた値で新しいノードを追加します。
def addAtIndex(self, index, val):
if index < 0 or index > self.size:
return # 無効なインデックスの場合は何もしません
if index == 0:
self.addAtHead(val)
elif index == self.size:
self.addAtTail(val)
else:
new_node = ListNode(val)
current = self.head
for _ in range(index - 1):
current = current.next
new_node.prev = current
new_node.next = current.next
current.next.prev = new_node
current.next = new_node
self.size += 1
# 連結リストの指定されたインデックスのノードを削除します。
def deleteAtIndex(self, index):
if index < 0 or index >= self.size:
return # 無効なインデックスの場合は何もしません
if self.size == 1:
self.head = self.tail = None
elif index == 0:
self.head = self.head.next
self.head.prev = None
elif index == self.size - 1:
self.tail = self.tail.prev
self.tail.next = None
else:
current = self.head
for _ in range(index):
current = current.next
current.prev.next = current.next
current.next.prev = current.prev
self.size -= 1
フルスクリーンモードを終える
これで両方向連結リストについての説明はおしまいです。次回もお会いしましょう。それでは、さようなら!
こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/iamadhee/doubly-linked-lists-4c8o