JavaScriptで単方向リンクリストから特定の値のノードを削除する方法
単方向リンクリストとは
連結リスト(リンクリスト)は、各ノードが「値(value)」と「次のノードへの参照(next)」を持つデータ構造です。配列と異なり、要素の追加や削除を参照の付け替えだけで行えるのが特徴です。ここでは、次のような単方向リンクリストを例に考えます。
const list = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: {
value: 5,
next: {
value: 6,
next: {
value: 7,
next: null
}
}
}
}
}
}
};
やりたいこと
リンクリストを第1引数、数値を第2引数として受け取る関数を作成します。この関数は、指定された値を持つノードがリスト内に存在するかどうかを先頭から探索し、存在すればそのノードをリストから取り除いてtrueを返します。最後まで見つからなければfalseを返します。
実装コード
再帰呼び出しを使って実装すると、次のようになります。
const removeNode = (list, val, prev = null) => {
// 末尾まで到達しても該当する値が見つからなかった場合
if (!list) {
return false;
}
if (list.value === val) {
if (prev) {
// 前のノードのnextを、削除対象の次のノードへ付け替える
prev.next = list.next;
} else if (list.next) {
// 先頭ノードが対象の場合は、次ノードの内容を先頭へコピーする
list.value = list.next.value;
list.next = list.next.next;
} else {
// 要素が1件しかないリストの場合
list.value = undefined;
}
return true;
}
// 一致しなければ次のノードへ進む(現在のノードを前ノードとして渡す)
return removeNode(list.next, val, list);
};
console.log(removeNode(list, 3)); // true
console.log(JSON.stringify(list, undefined, 4));
console.log(removeNode(list, 100)); // false(存在しない値)
コードのポイント
- 削除の仕組み: ノードを物理的に消すのではなく、「前のノードのnext参照を、削除対象ノードの次のノードへ向き直す」ことでリンクを切り離します。
- 先頭ノードの削除: 先頭には前のノードが存在しないため、次のノードの値と参照を先頭オブジェクトにコピーして対応します。
- 計算量: 先頭から1ノードずつ辿っていくため、時間計算量はO(n)です。
出力結果
コンソールには次のように出力されます。
true
{
"value": 1,
"next": {
"value": 2,
"next": {
"value": 4,
"next": {
"value": 5,
"next": {
"value": 6,
"next": {
"value": 7,
"next": null
}
}
}
}
}
}
値が3のノードが正しく取り除かれ、リストは「1 → 2 → 4 → 5 → 6 → 7」という順序になりました。存在しない値を指定した場合は、リストは変更されずにfalseが返されます。
-
JavaScriptで学ぶ循環型単一リンクリスト(Circular Singly Linked List)の基本
循環型単一リンクリストとは? 循環型単一リンクリスト(Circular Singly Linked List)とは、通常の単一リンクリスト(片方向連結リスト)を変形させたデータ構造です。最大の特徴は、最後のノードのnextポインタが最初のノードを指すという点にあります。 一般的な単一リンクリストでは、末尾ノードのnextポインタはnullを指し、そこでリストが終了します。しかし循環型の場合、このnextポインタが先頭ノードへと接続されるため、リスト全体がひとつの輪(リング)のように連なり、終端のない環状構造になります。 通常の単一リンクリストとの違い 終端の扱い: 通常のリストでは末尾ノ
-
JavaScriptで双方向連結リストの要素を削除する方法
双方向連結リストから要素を削除する仕組み連結リストからの要素削除は非常にシンプルです。やるべきことは「削除したいノードへの参照を失わせる」こと、つまり対象ノードをリンクのチェーンから切り離すだけです。ただし、削除する位置によって処理が異なるため、次の3つのケースを考慮する必要があります。先頭(head)の要素を削除する: head = head.next と代入するだけで、先頭ノードへの参照は失われ、headは2番目の要素を指すようになります。このとき、新しいheadのprevをnullに設定し、前方向のリンクも忘れずに切っておきます。末尾(tail)の要素を削除する: 後ろから2番目のノード