リンクリストの基本
序章
リンク リストは、物理ストレージ ユニット上の非順次および非順次のストレージ構造であり、データ要素の論理的順序は、リンク リスト内のポインタのリンク順序を通じて実現されます。
リンク リストは一連のノードで構成され (リンク リストの各要素はノードと呼ばれます)、ノードは実行時に動的に生成できます。各ノードは 2 つの部分で構成されます。1 つはデータ要素を格納するデータ フィールドで、もう 1 つは次のノードのアドレスを格納するポインタです。
リンク リストの挿入と削除の時間計算量は O(1) ですが、ランダム アクセスの時間計算量は O(n) です。
単一リンクリスト
写真に示すように (写真は Geek Time の「データ構造とアルゴリズムの美しさ」コラムから引用)
二重リンクリスト
写真の通り(写真はGeek Timeの「データ構造とアルゴリズムの美しさ」コラムより引用)
循環リンクリスト
写真の通り(写真はGeek Timeの「データ構造とアルゴリズムの美しさ」コラムより引用)
二重リンクリスト
写真の通り(写真はGeek Timeの「データ構造とアルゴリズムの美しさ」コラムより引用)
一般的なアルゴリズムのアイデア
センチネルポインタ
例 1: 「Jianzhi Offer」のアルゴリズム質問リンク リスト内の重複ノードを削除する
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头
指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
連結リストに関連するアルゴリズムは一般に比較的単純ですが、境界条件に注意してください。例 1 を例として挙げると、考えられる特殊なケースに注意する必要があります。
- リンクされたリストのヘッダーが繰り返されます: 1->1->1->1->2 は処理後は 2 になります
- リンクされたリストはすべて繰り返されます。1->1->1->1->1 は処理後に null になります。
図に示すように、センチネル ノードを追加します。ケース 1 またはケース 2 に関係なく、next
結果はセンチネル ノードのポインタを介して取得されます。
コードは以下のように表示されます
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead)
{
if(pHead == null)return null;
ListNode root = new ListNode(-1);//充当哨兵结点
root.next = pHead;
ListNode preNode = root;
ListNode nowNode = root.next;
while(nowNode != null&&nowNode.next != null){
ListNode nextNode = nowNode.next;
if(nextNode.val == nowNode.val){
while(nextNode != null&&nextNode.val == nowNode.val){
nextNode = nextNode.next;
}
preNode.next = nextNode;
nowNode = nextNode;
}else{
preNode.next = nowNode;
preNode = preNode.next;
nowNode = nowNode.next;
}
}
return root.next;
}
}
スピードポインター
高速ポインタと低速ポインタの考え方は、リンク リスト関連のアルゴリズムで非常に一般的であり、その考え方は、高速ポインタと低速ポインタを使用してリンク リストを走査し、必要な操作を完了することです。
例 2: 高速ポインタと低速ポインタを使用して、リンク リストの中間ノードを検索します。
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
リンクされたリストの長さを n、高速ポインタの速度を 2/時間、低速ポインタの速度を 1/時間と仮定します。そうすれば、小学校の算数は、高速ポインタがリンク リストの最後に到達すると、低速ポインタがリンク リストの中間点に到達することを知ることができます。それでも理解できない場合は、n = 10 または 9 などのデータを仮定して検証できます。
コードは以下のように表示されます
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode middleNode(ListNode list) {
if (list == null)return null;
ListNode fast = list;
ListNode slow = list;
while (fast != null){
fast = fast.next;
if (fast == null)break;
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
例 3: 高速ポインタと低速ポインタを使用して、リンク リスト内のリングのエントリ ノードを検索します。
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
このアルゴリズムの問題を解決するには、次の 2 つの手順があります。
最初のステップ: 指輪があるかどうかを判断する
高速ポインタの速度は 2/回、低速ポインタの速度は 1/回です。これらは同時にポイント A から開始します。リンクされたリストにリングがある場合、高速ポインタと低速ポインタはポイント C で出会う必要があります (ポイント C はリング内の任意のノードです)。リングがない場合は、高速ノードが無効である。コードは以下のように表示されます。
ListNode fast=pHead;
ListNode low=pHead;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
low=low.next;
if(fast==low)
break;
}
if(fast==null)//判断是否存在环
return null;
ステップ 2: リンク リストでリングのエントリ ノードを見つける
会うとき:
高速ノードが通過するパスは次のとおりです。AB + k*(BC+CB) + BC,(其中 k >= 1,k为整数)
低速ノードが通過するパスは次のとおりです。AB + BC
高速ポインタは低速ノードの 2 倍高速であるため、2*(AB + BC) = AB + K*(BC+CB) + BC
、つまりAB = (k-1)(BC+CB) + CB
。であるためk-1 >= 0
、2 つのポインタはそれぞれ点 A と点 C から始まり、点 B で交わる必要があります。
完全なコードは次のとおりです
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode fast=pHead;
ListNode low=pHead;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
low=low.next;
if(fast==low)
break;
}
if(fast==null||fast.next==null)//判断是否存在环
return null;
low=pHead;
while(fast!=low){
fast=fast.next;
low=low.next;
}
return low;
}
}
習得する必要があるコードの実装
- 単一リンクリスト、循環リンクリスト、二重リンクリストを実現し、追加および削除操作をサポートします。
- 単一リンクリストの反転を実装する
- 2 つの並べ替えられたリンク リストを 1 つの並べ替えられたリンク リストにマージします
- 連結リストの中間ノードを実現する
- リンクリスト内のリングの検出
- リンクされたリストの最後の n 番目のノードを削除します
面接でよくテストされる、リンクされたリストに関連するアルゴリズムの質問
- 循環リンクリスト
- k個のソートされたリンクリストをマージします
- リンクリスト内の重複ノードを削除する
- リンクリストのリングのエントリノード
- 2 つのリンクされたリストの最初の共通ノード
- 複雑なリンクリストの複製
- リンクされたリストの k 番目の最後のノード
- リンクされたリストを最後から最初まで印刷します
参考
- リートコード
- にうけ.com
- 「ソードポインターオファー」
- Geek Timeのコラム「データ構造とアルゴリズムの美しさ」