コンテンツ
(1)遅いのは一度に1ステップ、速いのは一度に2ステップですが、追いつくことができますか?
(2)遅いのは一度に1ステップ、速いのは一度に3ステップですが、追いつくことができますか?一度に4ステップはどうですか?nステップはどうですか?
(3)リンクリストループのエントリポイントはどこにありますか?
1.例の紹介
- 直接リンク:
- トピック:
2.リング付きのリンクリストとは何ですか?
通常の単一リンクリストでは、次のように、各ノードが順番にリンクされ、最後のノードがNULLを指します。
リングのあるリンクリストの最後のノードはNULLを指していませんが、前のノードを指しているため、リングのあるリンクリストが形成され、サイクルが続行されます。次のように:
3.問題解決のアイデア
上の写真を少し抽象化すると、リングに入る前は直線で表現し、リングに入った後は円でサイクルを表現します。この質問では、中間ノードを見つけて、前に説明した最後のk番目のノードの速度ポインターを見つけるというアイデアを使用する必要があります。位置の先頭を指すように、低速と高速の2つのポインターを定義します。ゆっくりと一度に1つのステップを実行し、高速で一度に2つのステップを実行します。
スローが直線の途中まで歩くとき、ファストはリングの入り口にあります。
スローがリングのエントリポイントに到達したときに、ファストが次の位置に移動し、その時点でファストが追いつき始めると仮定します。
次の位置でfastがslowに追いつき始めると仮定すると、fastはslowに追いつき始めます
- コードは次のように表示されます。
bool hasCycle(struct ListNode *head) { struct ListNode*slow=head; struct ListNode*fast=head; while(fast&&fast->next) { slow=slow->next; fast=fast->next->next; if(slow==fast) { return true; } } return false; }
崩壊だけの観点からは、この問題は複雑ではありません。これは、高速および低速のポインターのアイデアを使用することによってのみ解決できます。この問題だけでも、議論に値する多くの問題につながる可能性があり、循環リンクリストの理解次の3つの拡張問題を知ってください。
4.拡張の問題
(1)遅いのは一度に1ステップ、速いのは一度に2ステップですが、追いつくことができますか?
- 回答:確かに。
- 証明:
スローが真ん中に行くと、ファストはリングに入る必要があり、ファストは追いかけ始めます。スローがループに入った後、スローとファストの間の距離はNであると仮定します。このとき、スローは1ステップ、ファストは2ステップかかり、それらの間の距離は1短縮され、N-1になります。など、追跡するたびに距離が1ずつ減少し、距離が0に減少すると追いつきます。全体として、それは間違いなく追いつくでしょう。
(2)遅いのは一度に1ステップ、速いのは一度に3ステップですが、追いつくことができますか?一度に4ステップはどうですか?nステップはどうですか?
- 回答:必ずしも
- 証明:
まず、低速が一度に1ステップ、高速が一度に3ステップかかる状況について説明します。スローが1ステップ、ファストが3ステップでリングに入るだけで、スローがリングに入ると、ファストが1サークル歩いた可能性があります。具体的な状況は、リングのサイズによって異なります。このとき、スローとスローの間の距離速いのはNです。リングの長さをCと仮定します。
遅いのは一度に1ステップ、速いのは一度に3ステップで、距離はN-2になります。速く走ったり遅くなったりするたびに、距離が2つ短くなることがわかります。この時点で、見つけるのは難しくありません。分類して議論する必要があります。Nが偶数の場合は、追いつくことができます。Nが奇数の場合、追跡の最終距離は-1です。今回はもう一度追いかける必要があります。つまり、ゆっくりと速くなります。距離はC-1になります。
前の分析によると、C-1が偶数であれば、追いつくことができます。C-1が奇数の場合、追いつくことはなく、ワイヤレスループで追跡されますが、追いつくことはありません。それらの間の差Nは、リングに入る前の長さとリングの長さによって決定され、これら2つは両方ともランダムであるため、Nの値は不確実であり、奇数または偶数になる可能性があります。今、奇数は永遠に消えます。
同様に、4歩ずつ速く歩くことも同じ議論であり、必ずしもそうとは限りませんが、今回は歩くたびに距離が3分短くなります。Nが3の倍数の場合は追いつくことができます。3の倍数でない場合は、話し合いを続ける必要があります。興味のある子供靴は勉強を続けることができます。考え方は、一度に3つのステップをすばやく実行するのと同じです。 。
(3)リンクリストループのエントリポイントはどこにありますか?
低速と高速の移動距離を計算すると、エントリポイントは自然に明確になります。
- 法1:
低速は一度に1ステップ、高速は一度に2ステップ、高速は低速の2倍の距離を移動します
詳細に説明する前に、まず、低速ポインタが低速で円を歩くというようなことはなく、高速ポインタが低速に追いついていないことを明確にする必要があります。これは、高速が2を取るためです。一度に1歩ずつ、ゆっくりと1歩ずつ進みます。距離は毎回1ずつ減少するので、引っ掛かるまでどんどん近づいていきます。せいぜい1ラップ速くなりますが、正確に1ラップになることはありません。したがって、どれだけ遅く、速く進んだかを推測するのは簡単です。
- 仮定する:
- [リストヘッド----エントリポイント]:L
- [エントリポイント----エンカウンターポイント]:X
- 【リングの長さ】:C
低速走行距離:L + X
高速移動距離:L + N * C + X
- 説明:
スローは完全には進まず、追いかけられないことは先に述べたので、スローの距離はL+Xであると簡単に推測できます。
高速ポインタが一度に2ステップ移動する場合、リングが小さすぎるため、低速ポインタがエントリポイントに入る前に高速ポインタが数回歩いた可能性があります。短い3文で:
- Lは非常に小さく、Cは非常に大きく、低速がリングに入る前に、高速がリングの内側にある可能性があり、円は完成していません。
- Lは非常に大きく、Cは非常に小さく、ゆっくりとリングに入る前に、速く多くの円を歩きました
- しかし、スローがリングに入った後、1つの円内で、ファストはスローに追いつく必要があり、それらの距離は最大でC-1です。
最初に言ったことによると、速い距離は遅い距離の2倍であり、次の式をリストできます。
2 *(L + X)= L + N * C + X
簡略化後: L + X = N*CまたはL=N *C-XまたはL=(N-1)* C +(CX)またはL + X = N * C
この式を使用して、次のことを証明します。ポインターは出会いから、ポインターは頭から、エントリーポイントで出会います。
式(N-1)* Cは、集合点からN-1円を歩いた後、集合点に戻ることを示しているため、このとき、CXの距離は入口点に戻ります。上記のように、この式はそれらをエントリポイントに戻します。
- 実用的な問題を使用して、入口と出口の位置を具体的に解決します。
- 直接リンク:
- トピック:
- コードは次のように表示されます。
struct ListNode* detectCycle(struct ListNode* head) { struct ListNode* slow = head; struct ListNode* fast = head; while (fast && fast->next) { //判断是否是带环链表 slow = slow->next; fast = fast->next->next; if (slow == fast) { struct ListNode* meet = slow; while (meet != head) { //求出相遇点 meet = meet->next; head = head->next; } return meet; } } return NULL; }
- 待ち合わせ場所を見つける別の方法があります。
待ち合わせ場所を見つけたら、待ち合わせ場所をテールにし、次のポイントを新しいリンクリストの先頭にします。
この方法は特に独創的であり、2つのリンクリストの共通部分を見つけるという問題になります。この時点でheadAリンクリストのテールが一致し、headBリンクリストのテールも一致するため、2つのリンクリストが交差する必要があり、交差がエントリポイントになります。2つのリンクリストの交差リストは、ブロガーが以前のブログ投稿で詳述したものとまったく同じです。説明、ここではあまり強調しません。