データ構造とアルゴリズム (3) - リンクリストと関連アルゴリズム

リンクリストの基本

序章

リンク リストは、物理ストレージ ユニット上の非順次および非順次のストレージ構造であり、データ要素の論理的順序は、リンク リスト内のポインタのリンク順序を通じて実現されます。

リンク リストは一連のノードで構成され (リンク リストの各要素はノードと呼ばれます)、ノードは実行時に動的に生成できます。各ノードは 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->1->2 は処理後は 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 番目のノードを削除します

面接でよくテストされる、リンクされたリストに関連するアルゴリズムの質問

参考

  • リートコード
  • にうけ.com
  • 「ソードポインターオファー」
  • Geek Timeのコラム「データ構造とアルゴリズムの美しさ」

おすすめ

転載: blog.csdn.net/lichukuan/article/details/127063354