Stackoverflowの内部と外部のいくつかの投稿から、リンクリスト内のサイクル、つまりサイクルの長さを検出する方法がわかりました。また、ループの開始を検出する方法についても見つけました。
参考のために、もう一度手順を示します。
検出ループ:
伝統的にウサギとカメと呼ばれる2つのポインターがあります。ウサギを2ステップ移動し、亀を1ずつ移動します。ある時点で出会った場合は、必ずサイクルがあり、出会いのポイントは明らかにサイクルの内側にあります。
ループの長さを見つける:
1つのポインターをミーティングポイントに固定したまま、もう1つのポインターが同じになるまでインクリメントします。進むにつれてカウンターをインクリメントします。出会ったときのカウンター値はサイクルの長さになります。
サイクルの開始を見つける
1つのポインターをリストの開始点に移動し、もう1つのポインターを待ち合わせポイントに置きます。これで、1つずつ増加し、ミートポイントがループの開始点になります。いくつかのケースを使用して方法を紙で検証しましたが、なぜそれが機能するのか理解できません。
誰かがこの方法が機能する理由について数学的な証明を提供できますか?
ミーティングポイントを使用しない場合は、「サイクルの開始を見つける」証明を簡単にすることができます。
2番目のポインターを「ミーティングポイント」ではなく開始しますが、M
は最初のポインターの前に進みます。 (ここで、M
はループの長さです。)
このように、証明はかなり明白です。最初のポインターがループの開始に到達すると、2番目は正確にM
ステップ先になります。これもループの開始時です。
最初のノードへのポインタでリストを表す場合(list)
ループを検出するアルゴリズムは次のとおりです。
このアルゴリズムが正しいと仮定しましょう。このスキームでは、ループ状況は次の図で表されます。
ループの開始を指すノードを除くすべてのノードに、ターゲットの数から1を引いた数のラベルが付けられていることに注意してください。したがって、1つのノードにiのラベルが付けられていて、それがループの開始ではない場合、そのノードはi-1のラベルが付けられたノードによって次の要素としてポイントされます。
上記で説明したアルゴリズムは、そのメインステップ(3)が終了条件が満たされるまで繰り返されるサブステップのセットであるため、ループとして説明できます。これにより、アルゴリズム実行の反復数(t)の関数でpFastおよびpSlowを表す必要があります。
リストにループがない場合、ポインターの位置はtの関数で次のように記述されます。
ただし、ループが開始するノードがあり、その関数はポインターの進化の記述を停止します。このポインターがmでタグ付けされていると仮定すると、ループにはノードが含まれます(つまり そして )、およびt =を反復値として設定すると、 次に:
記述されたアルゴリズムを使用してループを検出するのに1つのポインターが確かに十分である場合、それは少なくとも方程式の解が存在している必要があります。 。
この方程式は、tの値が次のようになる場合にのみ当てはまります。
これは関数で終わり、 上記の方程式の有効な解であるtの値を生成します。
したがって、1つの低速ポインタと1つの高速ポインタで、リンクされたリストのループ状態を検出するのに十分であることが証明されています。
会議前にslowPointerが移動した距離= x + y
会議前にfastPointerが移動した距離=(x + y + z)+ y = x + 2y + z
FastPointerはslowPointerの2倍の速度で移動するため、がミーティングポイントに到達するまでの時間はどちらも一定です。
したがって、単純な速度、時間、距離の関係2(x + y)= x + 2y + z => x + 2y + z = 2x + 2y => x = z
したがって、slowPointerをリンクリストの先頭に移動し、slowPointerとfastPointerの両方が一度に1つのノードを移動するようにすると、どちらもカバーする距離が同じになります。
それらは、リンクされたリストでループが開始するポイントに到達します。
彼らが会ったときにそれが出発点であるというのは本当ではないと思います。しかし、他のポインター(F)が前のミーティングポイントにあった場合、そのポインターはループの開始ではなくループの最後にあり、リストの先頭から開始したポインター(S)はループの開始時に終了します。たとえば:
FをFastとして開始し、Sが9時に会議が終了するのと同じくらい遅い速度で開始します。 Sを最初から再開すると、ループの開始時に到達しますが、7になりますが、fは16になります。
私が間違っているかどうか教えてください
2番目の部分は、「ループの開始点を見つけるには、1つのポインターをリストの先頭に戻し、両方が出会うまで繰り返す」は間違っています。
正しい次の場合のみ高速ポインタがループを正確に1周した-ループの開始前の部分がループの長さよりも短い-しかし、それよりも長い場合は、アルゴリズム動作しません。無限ループに陥る可能性があります。
11個の要素を含むリンクリストで試してください。11番目は7番目を指します。
1 1-> 3 2-> 5 3-> 7 4-> 9 5-> 11 6-> 8 7-> 10 8-> 7 9-> 9 9
-ループが検出されました
1つ後ろに移動して開始し、インクリメントします。19-> 2 10-> 3 11-> 4 7-> 5 8-> 6 9-> 7 10-> 8 11-> 9 7-> 10 8-> 11 9-> 7 10-> 8 11-> ...