コードランダム記録アルゴリズム合宿4日目 | 24. リンクリストのノードをペアで交換、19. リンクリストの最後から2番目のNノードを削除、面接質問 02.07. リンクリストの交差、142. リングリンクリストII

今日学んだ記事とビデオへのリンク

24 記事リンク:リンク
24 ビデオ説明リンク:リンク
19 記事リンク:リンク
19 ビデオ説明リンク:リンクインタビュー質問 02.07 記事リンク:リンクインタビュー質問 02.07 ビデオ説明なし142 記事リンク:リンク142 ビデオ説明リンク:リンク



24. リンクリスト内のノードを 2 つずつ交換します

最初にタイトルを見てください

タイトル:
リンク リストを与え、その中で隣接するノードを 2 つずつ交換し、交換後のリンク リストの先頭ノードを返します。この演習は、ノード内の値を変更せずに (つまり、ノードの交換のみ) 完了する必要があります。

次のアイデアがあります:
2 つの隣接するノードの交換をループに書き込み、ペアごとの交換を完了します。ループ内の重要な点は、ノード ポインターと一時ノードのレコードの順序を変更することです

コードカプリスを読んだ感想

コードランダムレコードはポインタの操作手順を絵で示しており、ポインタの操作順序が一目でわかります。

最初に、 cur は仮想ヘッド ノードを指し、次の 3 つのステップを実行します。
ここに画像の説明を挿入
操作後のリンク リストは次のようになります。
ここに画像の説明を挿入
上記の 3 つのステップを通じて、隣接する 2 つの要素の交換が完了します。

次に、この操作をループするだけで、リンク リスト全体の隣接ノードのペアごとの交換が完了します。

導入中に遭遇した困難

ノード 2 がノード 3 から切断されているため、ステップ 2 のノード 1 の次のノードがノード 3 の位置を見つけることができないため、一時ノードは最初に記録されませんでした。

したがって、ノード ポインタを操作する前に、必ず一時ノードを記録してください。

ListNode* cur = dummyHead;

ポインター cur が仮想ヘッド ノードを指すように設定します。

コード

class Solution {
    
    
public:
    ListNode* swapPairs(ListNode* head) {
    
    
        ListNode* dummyHead = new ListNode(0);//设置一个虚拟头节点
        dummyHead->next = head;
        ListNode* cur = dummyHead;
        while(cur->next != nullptr && cur->next->next != nullptr){
    
    
            ListNode* tmp = cur->next;
            ListNode* tmp1 = cur->next->next->next;
			//以下三步与图中操作相对应
            cur->next = cur->next->next;
            cur->next->next = tmp;
            cur->next->next->next = tmp1;

            cur = cur->next->next;
        }
        return dummyHead->next;
    }
};

19. リンクリストの最後の N ノードを削除します

最初にタイトルを見てください

タイトル:
リンク リストを提供し、リンク リストのn最後から 2 番目の、リンク リストの先頭ノードを返します。

私は次のアイデアを持っています:
リンクリスト内の指定された相互ノードの削除は、高速ポインタと低速ポインタを介して実行できます。最初に高速ポインタをnステップ移動させ、次に低速が高速に達すると、高速と同時に移動し始めます。リンクされたリストの最後にある場合は、slow が指すポインターを削除するだけです。

コードカプリスを読んだ感想

コードのランダム記録は次のステップに分かれています。

  • 図に示すように、高速ポインターと低速ポインターを定義します。初期値は仮想ヘッド ノードです。
    ここに画像の説明を挿入
  • Fast は最初に n+1 ステップかかりますが、なぜ n+1 ステップなのかというと、図に示すように、同時に移動する場合にのみ、slow は削除されたノードの前のノードを指すことができるからです。

ここに画像の説明を挿入

  • 図に示すように、速い動きが終わりを指すまで、速い動きと遅い動きを同時に動かします。

ここに画像の説明を挿入

  • 図に示すように、slow が指す次のノードを削除します。

ここに画像の説明を挿入

導入中に遭遇した困難

実装するときは、高速に n+1 ステップを移動させてから、高速ポインタと低速ポインタを同時に移動させることに注意して、低速が削除されるノードの前のノードを指すことができるようにする必要があります。

次に、slow の次が削除されるノードの次を指すようにして、ノードの削除を完了します。

コード

class Solution {
    
    
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
    
    
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* slow = dummyHead;
        ListNode* fast = dummyHead;
        n++;
        while(n-- && fast != NULL){
    
    
            fast = fast->next;
        }
        while (fast != NULL){
    
    
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next;
        return dummyHead->next;
    }
};

インタビューの質問 02.07. リンクされたリストの交差

最初にタイトルを見てください

タイトルの説明:
2 つの単一リンクされたリストのヘッド ノードが与えられていheadAますheadB。2 つの単一リンクされたリストが交差する開始ノードを見つけて返してください。2 つのリンクされたリスト間に共通部分がない場合は、 を返しますnull

私は次のようなアイデアを持っています。
ポインタが交差したい場合、交差するノードから始まるノード内の要素はすべて等しい、さの差が n であると仮定して、2 つのリンクされたリストの長さの差を見つけて、長い方のリンク リストのポインタを n+1 の位置に移動し、次に 2 つのリンク リストのポインタを、2 つのポインタが出会うまで同時に移動して交点を見つけます。

コードカプリスを読んだ感想

考え方は私と同じです

導入中に遭遇した困難

困難はありませんでした

コード

class Solution {
    
    
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
    
    
        ListNode* curA = headA;
        ListNode* curB = headB;
        int lenA = 0, lenB = 0;
        while (curA != NULL) {
    
     // 求链表A的长度
            lenA++;
            curA = curA->next;
        }
        while (curB != NULL) {
    
     // 求链表B的长度
            lenB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;
        // 让curA为最长链表的头,lenA为其长度
        if (lenB > lenA) {
    
    
            swap (lenA, lenB);
            swap (curA, curB);
        }
        // 求长度差
        int gap = lenA - lenB;
        // 让curA和curB在同一起点上(末尾位置对齐)
        while (gap--) {
    
    
            curA = curA->next;
        }
        // 遍历curA 和 curB,遇到相同则直接返回
        while (curA != NULL) {
    
    
            if (curA == curB) {
    
    
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};

142. 循環リンクリスト II

最初にタイトルを見てください

タイトル説明:

リンク リストのヘッド ノードを指定するとhead、リンク リストがリングに入り始める最初のノードを返します。リンクされたリストが非巡回の場合は null を返します。

次のようなアイデアがあります。

  • リングの有無の判定:ファストポインタ法とスローポインタ法を使用して、先頭ノードから開始してファストポインタは一度に2ノードずつ移動し、スローポインタは一度に1ノードずつ移動します。 2 つのポインタが途中で交わると、リンクされたリストにはリングが形成されます。
  • リングのエントリを見つける方法: 2 つの重要なポイントがあります -リングのエントリ ノードポインタが交わるノードです

ヘッドノードからリングエントリノードまでのノード数をx、リングエントリノードから2つのポインタが交わるノードまでのノード数をy、遭遇ノードからリングエントリノードまでのノード数をyとする。 zです。

2 つのポインターが一致すると、低速ポインターによって渡されるノードの数は x+y となり、高速ポインターによって渡されるノードの数は x+y+n (y+z) になります。n はノードの数です
。高速ポインタがリング内で初めて低速ポインタに遭遇したことを示し、リング内を n 周歩きます。
(y + z) は円内のノードの数です。

そして、速いポインタは 2 歩、遅いポインタは 1 歩かかるため(物理的には、速いポインタの速度は遅いの 2 倍であることが理解できます)、速いポインタの移動距離は遅いポインタの 2 倍になります。
これにより、次の式が得られます:
(x + y) * 2 = x + y + n (y + z)

式を消去して転置すると、
x = n (y + z) - y が得られます。

式の引き算を避けるために、n(y+z) から 1 つの (y+z) を提案し、式を完成させた後の式は次のようになります。 x = (n - 1) (y + z) +
z

この式の意味は次のとおりです:ポインタはヘッド ノードから開始され、ポインタは会議ノードからも開始されます。これら 2 つのポインタは一度に 1 つのノードにのみ移動するため、2 つのポインタが出会うと、それがノードになりますリング入口の様子。

コードカプリスを読んだ感想

コード カプリスのアニメーションは、ポインタが交わる状況と円形の入り口を見つける方法をより直観的に示しています。

高速ポインタと低速ポインタが出会う:
ここに画像の説明を挿入
循環リンク リストのキー ノード間の距離の概略図:
ここに画像の説明を挿入
エントリ ノードを見つける概略図:
ここに画像の説明を挿入

導入中に遭遇した困難

この質問を見るのは二回目なので、ある印象を持っています。しかし、初めてこの質問を見たときに疑問に思ったのは、なぜ初めてリングで会うとき、ゆっくりとした歩数が x + 円数周分 + y ではなく、 x + y なのかということです。

その理由は次のとおりです。

まず、スローがリングに入ったとき、ファストはすでにリングに入っています。

遅い人がリングの入り口に入り、速い人がリングの入り口にもいる場合、次の図に示すように、リングを直線に拡張します。 遅い人と速い人の場合は、リングの入り口で歩き始めることがわかります
ここに画像の説明を挿入
。同時にリングに上がった場合、彼らは必ずリングの入り口 3 で会います。このとき、ゆっくり歩いて 1 周、速く歩いて 2 周します。

図に示すように、Slow がリングに入るとき、Fast はリングの任意の位置にあると仮定します。
ここに画像の説明を挿入
次に、Fast ポインタがリング エントリ 3 に到達すると、k + n 個のノードがすでに移動しており、それに応じて Slow が移動するはずです (k + n) / 2 ノード。

k < n であるため、(k + n) / 2 も n 未満でなければなりません。

これは、ゆっくり歩いた最初のリングで早く出会うことを意味します。

また、高速は低速をスキップしません。高速は一度に 2 つのノードを移動し、低速は一度に 1 ノードを移動するため、相対速度は、高速が一度に 1 ノードの速度で低速を追跡することになります

コード

class Solution {
    
    
public:
    ListNode *detectCycle(ListNode *head) {
    
    
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != NULL && fast->next !=NULL){
    
    
            slow = slow->next;
            fast = fast->next->next;
            if(slow==fast){
    
    
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while(index1!=index2){
    
    
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2;
            }
        }
        return NULL;
    }
};

今日は収穫

1. ペアワイズ交換リンク リストの一時ノード レコードの問題を思い出してください。

2. 連結リストの最後から2番目のノードを削除することで、ダブルポインタ方式の理解が再び深まります。

3. リンクリスト交差の特性を確認します。

4. 循環連結リスト この問題はより難しく、この問題を通じて循環連結リストの処理についての理解を深めます。

今日の勉強時間は3時間です

この記事の写真はすべて Carl のコード カプリスからのものです。本当に感謝しています。

おすすめ

転載: blog.csdn.net/m0_46555669/article/details/127030258