LeetCode142 リンク リスト サイクル II
作成者: Stefan Su
作成時間: 2022-10-29 02:40:13
場所:米国ニューヨーク州ニューヨーク市
説明Medium
head
リンクされたリストの を指定すると、サイクルが始まるノードを返します。サイクルがない場合はリターンしますnull
。
ポインタを継続的にたどることによって再度到達できるノードがリスト内にある場合、リンクされたリスト内にサイクルが存在しますnext
。内部的には、pos
tail のポインタが接続されているノードのインデックスを示すために使用されますnext
( 0 からインデックス付き)。-1
サイクルが無い場合です。pos はパラメータとして渡されないことに注意してください。
リンクされたリストは変更しないでください。
例1
Input: head = [3,2,0,-4], pos = 1
Output: tail connects to node index 1
Explanation: There is a cycle in the linked list, where tail connects to the second node.
例 2
Input: head = [1,2], pos = 0
Output: tail connects to node index 0
Explanation: There is a cycle in the linked list, where tail connects to the first node.
例 3
Input: head = [1], pos = -1
Output: no cycle
Explanation: There is no cycle in the linked list.
制約する
- リスト内のノードの数は
[0, 10
4]
の範囲内です。 -10
5<= Node.val <= 10
5pos
-1
またはリンクリスト内の有効なインデックスです。
分析
2点
- リンクされたリストが循環的かどうかを確認する
- リングがある場合、そのリングの入り口を見つける方法
詳細
-
リンクされたリストが循環的かどうかを確認する
2 ポインター メソッドを使用して、
fast
およびslow
ポインターをそれぞれ定義できます。head
ポインタはノードから開始してfast
一度に 2 つのノードを移動し、slow
ポインタは一度に 1 つのノードを移動します。fast
とslow
ポインタが途中で交わる場合は、リンクされたリストにリングがあることを意味します。なぜ
fast
2 つのノードに移動しslow
、1 つのノードに移動し、リングがある場合、永遠にずらされるのではなく、必ずリング内で合流するのでしょうか?まず第一に、最初のポイント:ポインタ
fast
は最初にリングに入らなければなりません。fast
ポインタとポインタが出会う場合slow
、それらはリング内で出会う必要がありますが、それは疑いの余地がありません。それでは、なぜポインタとポインタが一致する必要があるのか見てみましょう。
fast
slow
リングを描画して、任意のノードで
fast
ポインターがポインターに追いつき始めるようにすることができますslow
。
fast
そしてslow
それぞれが一歩踏み出しfast
てslow
出会うfast
二歩歩いてslow
一歩踏み出すからである。実際、 と比較するとslow
、fast
は に近いノードなのでslow
、fast
と重ねることができるはずですslow
。 -
リングがある場合、そのリングの入り口を見つける方法
この時点で、リンクリストにリングがあるかどうかが判断できるので、次のステップはこのリングのエントリを見つけることです。
ヘッドノードからリングエントリノードまでのノード数が であるとします
x
。fast
リング入口ノードからポインタが出会うノードまでのノード数は でslow
あるy
。エンカウントノードからリングエントリノードまでのノード数は ですz
。写真が示すように:次に、それらが出会うとき: ポインタによって渡されるノードの数は
slow
:x + y
、ポインタによって渡されるノードの数fast
:x + y + n (y + z)
、ここで、はポインタと出会うまでのリング内の円の後のポインタn
であり、円内のノードの数 A です。fast
n
slow
(y+z)
fast
ポインターは一度に 2 つのノードを移動し、slow
ポインターは一度に 1 つのノードを移動するため、fast
ポインターが通過するノードの数は、ポインターが通過するノードの数の 2 倍に等しくなりますslow
。(x + y) * 2 = x + y + n(y + z)
両側から 1 つを削除します
(x+y)
。x + y = n (y + z)
リングへの入口を見つける必要があるため、はヘッド ノードからリング入口ノードまでの距離を表す
x
ため、必須です。x
それで、 を求めて
x
、x
左側に単独で置いてください:x = n (y + z) - y
、(y+z)
次に、 fromを考え出しますn(y+z)
。式を整理すると、次の式になります。ポインターに出会うまでにポインターは少なくとももうx = (n - 1) (y + z) + z
1 周移動する必要があるため、ここでは n が 1 以上である必要があることに注意してください。fast
slow
この式は何を示しているのでしょうか?
例として、n が 1 の場合を考えてみましょう。これは、高速ポインタがリング内で回転した後、低速ポインタに遭遇することを意味します。
n が 1 の場合、式は次のように解決されます
x = z
。これは、ヘッドノードからポインタが設定され、エンカウントノードからもポインタが設定されることを意味する。これら 2 つのポインタは一度に 1 つのノードにしか移動しないため、これら 2 つのポインタが出会うと、それがリング エントリのノードになります。
つまり、遭遇ノードでポインタを定義し、ヘッド ノードに
index1
ポインタを設定します。index2
同時に 1 ノードずつ移動し、それらが出会う場所がリングの入り口のノードになります
index1
。index2
では、n が 1 より大きい場合、つまり、リング内を n 周した後で
fast
ポインターがポインターに遭遇した場合はどうなるでしょうか。slow
実際、この状況は n が 1 の場合と同じであり、この方法でリングの入口ノードを見つけることができますが、ポインタはリング内でさらに多くの回数
index1
回転し、その後 に遭遇します。集合点は依然として入口ノードです。リングの。(n-1)
index2
解決
- 2つのポインターのバージョン
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (slow == fast) {
ListNode index_1 = fast;
ListNode index_2 = head;
while (index_1 != index_2) {
index_1 = index_1.next;
index_2 = index_2.next;
}
return index_1;
}
}
return null;
}
}
LeetCode142 を解決する際に、このブログがインスピレーションになれば幸いです。ご質問がございましたら、以下にコメントしてください。