Pythonで連結リストを昇順にソートするプログラムの書き方
問題の概要
連結リスト(リンクリスト)が与えられたとき、そのリストを昇順に並べ替えることを考えます。
例えば、入力が [5, 8, 4, 1, 5, 6, 3] の場合、出力は [1, 3, 4, 5, 5, 6, 8] となります。
解決のアプローチ
この問題は、以下の手順で解くことができます。
- 新しい空のリスト
valuesを用意します。 headに先頭ノードへの参照を保存しておきます。nodeがnullでない間、次の処理を繰り返します。nodeの値をvaluesの末尾に追加します。nodeを次のノードに進めます。
valuesを昇順にソートします。valuesの要素から両端キュー(collections.deque)を作成します。nodeを先頭ノードheadに戻します。nodeがnullでない間、次の処理を繰り返します。- キューの左端から要素を取り出して削除し、その値を
nodeの値として設定します。 nodeを次のノードに進めます。
- キューの左端から要素を取り出して削除し、その値を
headを返します。
このアプローチのポイントは、ノード同士のリンク構造を一切変更せず、各ノードが保持している「値」だけを入れ替えている点です。ポインタのつなぎ替えが必要ないため、実装が非常にシンプルになります。
実装例
より理解を深めるために、以下の実装を見てみましょう。
import collections
class ListNode:
def __init__(self, data, next = None):
self.val = data
self.next = next
def make_list(elements):
head = ListNode(elements[0])
for element in elements[1:]:
ptr = head
while ptr.next:
ptr = ptr.next
ptr.next = ListNode(element)
return head
def print_list(head):
ptr = head
print('[', end = "")
while ptr:
print(ptr.val, end = ", ")
ptr = ptr.next
print(']')
class Solution:
def solve(self, node):
values = []
head = node
while node:
values.append(node.val)
node = node.next
values.sort()
values = collections.deque(values)
node = head
while node:
node.val = values.popleft()
node = node.next
return head
ob = Solution()
head = make_list([5, 8, 4, 1, 5, 6, 3])
print_list(ob.solve(head))
入力
[5, 8, 4, 1, 5, 6, 3]
出力
[1, 3, 4, 5, 5, 6, 8]
計算量について
このアルゴリズムでは、まず全ノードを一度走査して値を収集し(O(n))、続いてソートを実行し(O(n log n))、最後にもう一度ノードを走査して値を書き戻します(O(n))。したがって、全体の時間計算量は O(n log n)、空間計算量は O(n) となります。
マージソートなどを使ってリンクリストを直接ソートする方法もありますが、本手法はノードの接続関係を壊さずに済むため、コードの可読性と実装の容易さという点で大きなメリットがあります。
-
【Python】連結リストをジグザグ二分木に変換するプログラムの書き方
問題の概要単方向連結リスト(片方向リンクリスト)が与えられたとき、次のルールに従って二分木へ変換することを考えます。連結リストの先頭ノード(head)が、二分木のルートになります。それ以降の各ノードは、その値が親ノードより小さい場合は左の子に、そうでない場合は右の子になります。たとえば、入力が [2,1,3,4,0,5] の場合、変換後の二分木は次のような「ジグザグ」形状になります。解き方の手順この問題は、再帰的に呼び出す関数 solve() を定義すると、シンプルに解くことができます。具体的な手順は以下の通りです。ノードを引数として受け取る関数 solve() を定義します。ノードが nul
-
Pythonで連結リストのノードを削除する方法
連結リスト(リンクリスト)にいくつかの要素が格納されているとします。ここでの課題は、指定されたノードをリストから削除する関数を作成することです。例えば、リストが 1 → 3 → 5 → 7 → 9 の場合、値 3 を削除すると、結果は 1 → 5 → 7 → 9 になります。削除の基本的な考え方削除したいノードを指すポインタ「node」が与えられている場合、以下の2つの操作を実行することでノードを削除できます。node.val = node.next.val — 次のノードの値を現在のノードにコピーするnode.next = node.next.next — 現在のノードの参照先を、次の次のノ