edubun
日本語

連結リスト

連結リストの先頭に値を加えるときに変えるポインターは2つです。リストの値が4個でも400万個でも同じです。

次の値を指す値

配列は値を隣り合わせに並べるので、先頭を変えるのに費用がかかります。連結リストはこの決まりをなくします。それぞれの値はノードと呼ばれる小さな記録に入り、ノードはメモリのどこに置かれてもかまいません。MITの6.006の講義資料では、ノードには2つの欄があります。項目(item)と、次のノードを指すポインターの next です。リスト自体はheadと呼ばれる最初のノードだけを覚えています。最後のノードの next は null で、これが終わりの印です。

リストを読むには、head から始めて next ポインターを1つずつたどります。次のメモが隠してある場所を書いたメモを、順に追っていくようなものです。これには費用がかかります。4番目のノードの番地を求める式はないので、位置 i の値を得るには i + 1 個のノードを訪ねなければなりません。MITの資料では、連結リストの get at(i) は O(n) です。配列なら1ステップでできたことです。

// 値ごとに1つのノード: { value, next }
let head = null;
for (const v of [4, 30, 7, 12]) head = { value: v, next: head };
// リストをたどる
for (let n = head; n !== null; n = n.next) console.log(n.value); // 12, 7, 30, 4

つなぎ直してリストを変える

うまみが出るのはリストを変えるときです。先頭に新しい値を入れるには、next が今の head を指すノードを作り、head をその新しいノードに向けます。ポインターを2つ変えるだけで、ほかには何も動きません。先頭のノードを消すのは1回の変更で、head を head.next に向けるだけです。同じ操作ですべての値が動いた配列とくらべてみてください。

途中の位置では、場所を探すことが仕事になります。位置 i に挿入するには、そのすぐ前のノードまでたどり(i 回の訪問)、同じように2つのポインターを変えます。new.next = prev.next、次に prev.next = new です。この順番が大切です。先に prev.next を変えると、リストの残りを指すものがなくなり、残りが失われてしまいます。ラボでは、ノードを1つ訪ねるか、ポインターを1つ変えるのが1ステップです。だから4ノードのリストの位置2に挿入すると、訪問2回と変更2回で4ステップです。

読み込み中

ドラッグで回転、スクロールまたはピンチで拡大・縮小。

4個のノードの単方向連結リストです。スライダーで位置を選んで挿入や削除をしましょう。カウンターは訪ねたノードと変えたポインターを分けて数えます。「検索」は、値か null に出会うまで head からたどります。
操作配列(n個の値)連結リスト(n個のノード)
位置 i の値を得る1i + 1 回の訪問
先頭に挿入移動n回 + 書きこみ1回ポインターの変更2回
先頭を削除する読み1回 + 移動 (n − 1) 回ポインターの変更1回
位置 i に挿入する(n − i) 回の移動 + 書き込み1回i 回の訪問 + 変更2回
値を探す最大 n 回の読み取り最大 n 回の訪問

使いどころ

連結リストが向いているのは、変更のほとんどが先頭のように、すでにポインターを持っている場所で起こり、位置 i をめったに使わないときです。キューやスタックは連結リストで作ることができ、次のユニットのハッシュテーブルの中のチェインも同じです。いくつかの変種があります。双方向連結リストは各ノードに prev ポインターも持たせるので、逆向きにたどれ、ノードを手に入れていれば1ステップで削除できます。head に加えて tail ポインターも持っておくと、末尾への追加が安くなります。

ふだんのJavaScriptで連結リストを書くことはあまりありません。配列はよく最適化されていて、最近のプロセッサーのキャッシュは隣り合わせに保存された値に有利だからです。それでもこの考え方は大切です。木やグラフ、多くのライブラリの構造は、まさにこのようにノードとポインターから作られています。

背景連結リストは1950年代半ばに、情報処理言語(IPL)とともに登場しました。IPLは、1956年ごろRAND研究所とカーネギー工科大学で、アレン・ニューウェル、クリフ・ショー、ハーバート・サイモンが作りました。彼らのプログラムは論理の定理の証明を探すもので、覚えておくものの数や大きさを前もって知ることができませんでした。マスをつないだリストなら、メモリを1マスずつ渡したり取り戻したりできます。IPLのリスト処理は、数年後にジョン・マッカーシーが設計したLispに影響を与えました。
  1. 単方向連結リストのノードは、どの2つの欄を持っていますか?
    • item と next
    • index と value
    • left と right
    • head と tail
  2. リスト 12 → 7 → 30 → 4 があります。先頭に新しい値を挿入するのは何ステップですか(ポインターの変更)?
  3. 同じリストで、位置2に挿入するのは何ステップですか(訪問とポインターの変更の合計)?
  4. 同じリストで、30を探すと何個のノードを訪ねますか?
  5. 同じリストで、99を探すと null に着くまでに何個のノードを訪ねますか?
  6. 同じリストで、位置3のノード(4)を削除するのは何ステップですか?
  7. 余裕のある配列に1,000個の値が入っています。先頭に挿入するのは何ステップですか?
  8. 配列より連結リストに向いている仕事はどれですか?
    • 位置500の値を何度も読む
    • 先頭で何度も追加と削除をする
    • 二分探索
    • インデックスで並べかえる
  9. ノード p の後ろに挿入するとき、リストの残りを失わないのはどの順番ですか?
    • p.next = new、次に new.next = p.next
    • new.next = p.next、次に p.next = new
    • どちらの順番でもよい
    • 先に p を削除する
  10. 1956年ごろRANDで作られ、マスをつないだリスト処理を初めて取り入れた言語はどれですか?
    • Fortran
    • IPL
    • C
    • JavaScript
ポイント
  • 連結リストはそれぞれの値を next ポインターを持つノードに保存し、リストは head を覚えています。
  • 先頭での挿入や削除はポインターを1つか2つ変えるだけで、何も動きません。
  • 位置 i に着くには i + 1 回の訪問が必要なので、get at(i) は O(n) です。
  • 前のノードをつなぎ直す前に new.next を設定します。
このコースの出典 (11)
  • MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
  • lecture notes 2 Data Structures (interface vs data structure, Sequence and Set interfaces, stack = insert_last/delete_last, queue = insert_last/delete_first, array, linked list, dynamic array, amortized Θ(1) append)
  • 4 Hashing (chaining, pigeonhole collisions, chain length n/m)
  • 6 Binary Trees Part 1 (height, traversal order, O(h) operations) and 7 Binary Trees Part 2: AVL (Adelson-Velsky and Landis, 1962) at ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes
  • MDN Web Docs, Array.prototype.shift (shifts all values left by 1)
  • Array.prototype.unshift, Map (insertion order, Map vs Object, spec requires sublinear average access) and Set (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)
  • ECMAScript 2025 Language Specification (tc39.es/ecma262)
  • 24.1 Map Objects and 24.2 Set Objects ("must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements")
  • Array.prototype.shift steps (Repeat, while k < len: move element k to k - 1)
  • D. E. Knuth, The Art of Computer Programming vol. 3 (2nd ed. 1998) section 6.4 History (H. P. Luhn, IBM internal memorandum January 1953, hashing with chaining; digit pairs added mod 10, 31415926 -> 4548) via gwern.net copy, and IEEE Spectrum, "Hans Peter Luhn and the Birth of the Hashing Algorithm" (2018)
  • "A brief history of the stack", SIGCIS (sigcis.org/files/A brief history.pdf: Bauer and Samelson applied for the patent in 1957, US 3,047,228 granted July 31, 1962; IPL list processing by Newell, Simon and Shaw at RAND 1956) and Wikipedia, Information Processing Language (Newell, Shaw, Simon, RAND and Carnegie Institute of Technology, about 1956).

確認日: 2026-10-18