* Retranslate the complete Japanese online edition from Chinese * Fit Japanese homepage labels and localize heading permalinks * Point Japanese contribution instructions to the Japanese preview * Replace locale conditionals with multilingual UI tables
6.6 KiB
練習問題
知識の確認
配列と連結リストで要素を見つけるには
配列と単方向連結リストに、どちらも [A, B, C, D, E] が順に保存されています。4 番目の要素 D を読み取ります。
- 配列では、どのインデックスを直接使えますか。
- 単方向連結リストでは、先頭ノード
Aからnextをたどると、どのノードを順に通りますか。 - 読み取る要素の位置が後ろになるにつれ、各構造で必要な手順の数はどう変わりますか。位置を指定した読み取りを繰り返す用途にはどちらが適していますか。理由も説明してください。
??? success "解答例"
1. インデックスが 0 から始まるなら、4 番目の要素のインデックスは 3 なので、配列では `arr[3]` を直接読み取れます。
2. 単方向連結リストは先頭からたどる必要があります。訪問経路は `A → B → C → D` で、`next` に沿って 3 回進みます。
3. 配列は先頭アドレスとインデックスから要素の位置を直接求められるため、位置を指定したアクセスの時間計算量は $O(1)$ です。
単方向連結リストの $k$ 番目のノードにアクセスするには、先頭ノードから `next` に沿って $k-1$ 回進む必要があり、
最悪の場合は $O(n)$ 時間かかります。
ここで比較しているのは位置を指定した読み取りだけであり、連結リストがすべての操作で遅いという意味ではありません。
配列と連結リストに要素を挿入するには
配列と単方向連結リストに、どちらも A、B、C、D が保存されています。B の後ろに X を挿入します。
- 配列の容量は 5 で、現在の状態は
[A, B, C, D, _]です。 - 連結リストは
A → B → C → Dで、ノードBを指す参照をすでに取得しています。
- 配列では、どの要素を移動する必要がありますか。挿入後の配列を書いてください。
- 連結リストでは、
X.nextとB.nextをどの順番で変更しますか。挿入後の連結リストを書いてください。 - 挿入の効率を比較するとき、「ノード B の参照をすでに取得している」と明記するのはなぜですか。
??? success "解答例"
1. 配列では、まず `D` を右へ一つ、次に `C` を右へ一つ移動し、最後に `X` をインデックス 2 に置きます。
結果は `[A, B, X, C, D]` です。
2. 元の `B.next` は `C` を指しています。まず `X.next = B.next` として `X` を `C` につなぎ、
次に `B.next = X` とします。結果は `A → B → X → C → D` です。
元の接続を保存せずに先に `B.next` を上書きすると、`C` を見つけられなくなる可能性があります。
3. `B` の位置が分かっていれば、連結リストへの挿入は二つの接続を変更するだけなので、$O(1)$ 時間で完了します。
先頭から `B` を探す必要があれば、その探索自体に $O(n)$ 時間かかる場合があります。
リストの容量が増える仕組み
配列で実装したリストの現在の内容は [A, B, C]、長さは size = 3、容量は capacity = 4 です。
容量が足りなくなったら、新しい配列の容量を元の 2 倍にするものとします。
Dを追加すると、リストの長さと容量はそれぞれいくつになりますか。容量の拡張は必要ですか。- 続いて
Eを追加するとき、容量はいくつになりますか。元の要素を何個コピーする必要がありますか。 - 内部の配列の長さは変えられないのに、リストの容量が増えるように見えるのはなぜですか。
??? success "解答例"
1. `D` は最後の空き位置に置けます。内容は `[A, B, C, D]`、
`size = 4`、`capacity = 4` となり、容量の拡張は不要です。
2. さらに `E` を追加するときには空きがないため、容量 8 の新しい配列を作り、
元の `A、B、C、D` の計 4 要素をコピーしてから `E` を追加します。
このとき、`size = 5`、`capacity = 8` となります。
3. 元の配列自体が長くなったわけではありません。リストがより大きな配列を新しく作り、元の要素をコピーし、
内部の格納先を新しい配列に切り替えるため、利用者からは容量が増えたように見えます。
プログラミング演習
配列で表した大きな整数に 1 を足す
配列 digits には、非負整数の各桁が左から右へ保存されています。例えば [3, 0, 8] は 308 を表します。
0 は [0] で表し、それ以外の入力では先頭の桁は 0 ではありません。
十進数の筆算を模して、この整数に 1 を足し、結果を同じ配列の形式で返してください。
digits を直接変更して構いません。先頭に新しい繰り上がりが生じたら、より長い配列を返せます。
??? tip "解くためのヒント"
1. 筆算と同じように、配列の最後の桁から始めます
2. 現在の桁が 9 未満なら、1 を足してすぐに返せます
3. 現在の桁が 9 なら 0 に変えます。すべての桁が 9 なら、先頭に 1 を補う必要があります
LeetCode{ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" }
単方向連結リストの反転
単方向連結リストの先頭ノード head が与えられます。各ノードは、値と次のノードを指す next を持ちます。
反復によってノード間のすべての接続を反転し、反転後の先頭ノードを返してください。 新しい連結リストのノードは作らないでください。
??? tip "解くためのヒント"
1. まず紙に、つながった三つのノードと二つのポインタ prev、cur を描いてみましょう
2. cur.next を書き換える前に、元の次のノードを必ず nxt に保存します
3. cur.next を反転したら、prev = cur、続いて cur = nxt として、元の連結リストの次のノードを処理します
LeetCode{ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" }