二つのソート済み単方向リストのマージアルゴリズム

二つの非減少順(昇順)に整列された単方向連結リストを、一つの新たなソート済みリストに統合する問題。統合後のリストは、元の二つのリストに含まれるすべてのノードを再利用して構成され、追加のメモリ割り当ては不要である。 核心的なアプローチは以下の通り: - **ダミーノード**(仮想ヘッド)を導入し、新規リストの先頭を一貫して扱えるようにする - 結果リストを構築するための作業ポインタ `current` を用意 - 両リストの先頭要素を比較し、小さい方のノードを結果リスト末尾に連結 - 片方のリストが尽きた時点で、残りのノードをそのまま接続 以下はC++による実装例(オリジナルと異なる構造・命名・ロジックフローで再設計): #include <iostream> struct Node { int data; Node* link; explicit Node(int d) : data(d), link(nullptr) {} }; Node* mergeSortedLists(Node* first, Node* second) { Node sentinel(-1); // ダミー先頭ノード(スタック確保) Node* tail = &sentinel; while (first && second) { if (first->data <= second->data) { tail->link = first; first = first->link; } else { tail->link = second; second = second->link; } tail = tail->link; } tail->link = (first) ? first : second; return sentinel.link; } void display(Node* head) { while (head) { std::cout << head->data << " "; head = head->link; } std::cout << "\n"; } int main() { // リスト1: 1 → 3 → 5 → 7 Node* a = new Node(1); a->link = new Node(3); a->link->link = new Node(5); a->link->link->link = new Node(7); // リスト2: 2 → 4 → 6 → 8 → 9 Node* b = new Node(2); b->link = new Node(4); b->link->link = new Node(6); b->link->link->link = new Node(8); b->link->link->link->link = new Node(9); Node* merged = mergeSortedLists(a, b); display(merged); // 出力: 1 2 3 4 5 6 7 8 9 return 0; } この実装の特徴: - ダミーノードをスタック上に配置し、ヒープ割り当てを回避(メモリ管理の簡素化) - `sentinel` と `tail` の名前で可読性を向上 - 条件分岐を `<=` に変更し、等値時の安定性を明示的に保証 - リスト構築後、元のポインタ `a`, `b` は無効化される点に注意(所有権移転) - 時間計算量は O(m + n)、空間計算量は O(1) — 新規ノード生成なし ダミーノードの利点は、初期状態における「空リスト」への対応を完全に抽象化できることにある。すなわち、`tail->link` への代入操作が常に有効であり、`head == nullptr` の分岐やポインタの二重管理を不要とする。これは特に反復処理系のコード品質と保守性を高める重要な設計パターンである。

タグ: linked-list merge-sort cpp Algorithm data-structures

5月15日 08:26 投稿