[間違い訂正] リンク リストに閉ループがあるかどうか、および閉ループが発生する場所を確認するにはどうすればよいですか?

2つのポインタp1、p2(毎回1、2ずつ増加)を使用して判定します。

2 つのポインターを使用します: 低速と高速、低速は一度に 1 ビットずつ移動し、高速は一度に 2 ビットずつ移動し、次の条件のいずれかが発生すると終了し、時間計算量は O(n) です。

  • 終了の最初の条件は、ポインター p2 が NULL ノードに遭遇することです。これは、閉ループが存在しないことを意味します。
  • 別の条件式は、2 つのポインターが一致すると終了します。これは、閉ループが存在することを意味します。

なぜ指輪があるのに二人は会わなければならないのでしょうか?ファストが先にリングに入場するので、スローが入場した後、スローを先頭とすると、次のサイクルでファストは毎回1ずつスローに近づくため、必ずミートし、ファストが直接スローをスキップすることはありません。

def checkclosedcycle(head):
    """检查当前链表是否存在闭环。"""
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow is fast and fast is not None:
            return True
    return False

拡張された質問

LeetCode の質問

リンクリストサイクル

リンクされたリストが与えられた場合、その中に循環があるかどうかを判断します。

フォローアップ: 余分なスペースを使用せずに解決できますか?

単一リンクリストにサイクルがあるかどうかを確認するにはどうすればよいですか?

リンクリストサイクル II

リンクされたリストを指定して、サイクルが開始するノードを返します。サイクルがない場合はリターンします null

フォローアップ: 余分なスペースを使用せずに解決できますか?

リングの最初のノードを見つけるにはどうすればよいでしょうか?

この問題に関連する質問をインターネット上でいくつか集めてみたところ、私の考えが大きく広がりました。

1. リングの長さはどれくらいですか?

2. リング内の最初のノード (つまり、リンク リスト サイクル II) を見つけるにはどうすればよいですか?

3. リングのある連結リストを単一リンクリストに変える (リングを削除する) にはどうすればよいですか?

4. 2 つの単一リンクリスト間に共通部分があるかどうかを判断するにはどうすればよいですか? 最初に交差するノードを見つけるにはどうすればよいでしょうか?

まず、次の図を見てみましょう。

リンクされたリストの先頭がX、リングの最初のノードがY、低速と高速の最初の交点がZであるとします。図に示すように、各セグメントの長さはそれぞれa、b、cで​​す。ループの長さはLです。低速と高速の速度はそれぞれ qs、qf です。

それぞれの問題を一つずつ分析してみましょう。

1. リングの長さはどれくらいですか?

方法 1 (この答えはインターネット上にあります):

最初の遭遇後、低速、高速を継続し、次の遭遇までのサイクル数を記録します。なぜなら、速い人が 2 回目に Z 点に到達すると、速く 1 周歩き、ゆっくりと半周歩き、3 回目に Z 点に到達すると、速く 2 周し、ゆっくりと 1 周歩き、ちょうど Z 点で出会うからです。

方法 2:

最初の遭遇後、速く停止し、ゆっくりと歩き続け、次の遭遇までのサイクルの回数を記録します。

方法 3 (最も単純): 初めて会ったときに遅い人が移動した距離: a+b、速い人が移動した距離: a+b+c+b。

速い速度は遅い速度の2倍なので、速い速度で進む距離は遅い速度の2倍となり、2(a+b) = a+b+c+bとなり、a=cが得られます(この結論は非常に重要です! )

L=b+c=a+b、つまり、最初から 2 つの最初の遭遇までのサイクル数がリングの長さに等しいことがわかります。

注: 実際に方法 3 をテストしたところ、実際にはバグがあることがわかりました。ここで、元の作成者は、遅いと速いが初めて出会った場合、速い場合は遅いよりも 1 周だけ多くかかるようにデフォルト設定しています。ただし、実際には例外があります。つまり a>b+ c のとき、slow が Y 点に到達したとき、fast はすでに 2a>2(b+c) 、つまり 2 周以上経過しています。 1 周以内では、上記の推論 a=c も真ではありません。したがって、何かが間違いになるはずです。

実験結果については、コードのフォローアップを参照してください。

def getclosedcyclelength(head):
    """获取当前链表中闭环的长度。"""
    slow = fast = head
    length = 0
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        length += 1
        if slow is fast and fast is not None:
            return length
    return 0

 上記のバグに対応して、次の実験コードを設計しましたが、その結果から、Y の位置が合計の 1/2 を超えると、結果は常に間違っていることがわかります。

def test_checkclosedcycle(Total, Y):
    a = LinkedList(range(Total))
    # print('a:', a[:Y], ' len:', Y)
    y = a[Y]
    # str_y = ListNode.str_nodes(y)
    # print('b+c:{', str_y, '} len:', str_y.count(',') + 1)
    a.extend(y)
    # print('abc:', a, ' len:', len(a))
    L = getclosedcyclelength(a.head)
    print('cycle pos Y: {}, cycle len L: {}'.format(Y, L))

def main():
    for Total in range(15):
        print('* Total Length is {}, when Y > {} L goes wrong'.format(Total, Total//2))
        for Y in range(Total):
            test_checkclosedcycle(Total, Y)

main()

結果は次のとおりです。 

* Total Length is 0, when Y > 0 L goes wrong
* Total Length is 1, when Y > 0 L goes wrong
cycle pos Y: 0, cycle len L: 1
* Total Length is 2, when Y > 1 L goes wrong
cycle pos Y: 0, cycle len L: 2
cycle pos Y: 1, cycle len L: 1
* Total Length is 3, when Y > 1 L goes wrong
cycle pos Y: 0, cycle len L: 3
cycle pos Y: 1, cycle len L: 2
cycle pos Y: 2, cycle len L: 2
* Total Length is 4, when Y > 2 L goes wrong
cycle pos Y: 0, cycle len L: 4
cycle pos Y: 1, cycle len L: 3
cycle pos Y: 2, cycle len L: 2
cycle pos Y: 3, cycle len L: 3
* Total Length is 5, when Y > 2 L goes wrong
cycle pos Y: 0, cycle len L: 5
cycle pos Y: 1, cycle len L: 4
cycle pos Y: 2, cycle len L: 3
cycle pos Y: 3, cycle len L: 4
cycle pos Y: 4, cycle len L: 4
* Total Length is 6, when Y > 3 L goes wrong
cycle pos Y: 0, cycle len L: 6
cycle pos Y: 1, cycle len L: 5
cycle pos Y: 2, cycle len L: 4
cycle pos Y: 3, cycle len L: 3
cycle pos Y: 4, cycle len L: 4
cycle pos Y: 5, cycle len L: 5
* Total Length is 7, when Y > 3 L goes wrong
cycle pos Y: 0, cycle len L: 7
cycle pos Y: 1, cycle len L: 6
cycle pos Y: 2, cycle len L: 5
cycle pos Y: 3, cycle len L: 4
cycle pos Y: 4, cycle len L: 6
cycle pos Y: 5, cycle len L: 6
cycle pos Y: 6, cycle len L: 6
* Total Length is 8, when Y > 4 L goes wrong
cycle pos Y: 0, cycle len L: 8
cycle pos Y: 1, cycle len L: 7
cycle pos Y: 2, cycle len L: 6
cycle pos Y: 3, cycle len L: 5
cycle pos Y: 4, cycle len L: 4
cycle pos Y: 5, cycle len L: 6
cycle pos Y: 6, cycle len L: 6
cycle pos Y: 7, cycle len L: 7
* Total Length is 9, when Y > 4 L goes wrong
cycle pos Y: 0, cycle len L: 9
cycle pos Y: 1, cycle len L: 8
cycle pos Y: 2, cycle len L: 7
cycle pos Y: 3, cycle len L: 6
cycle pos Y: 4, cycle len L: 5
cycle pos Y: 5, cycle len L: 8
cycle pos Y: 6, cycle len L: 6
cycle pos Y: 7, cycle len L: 8
cycle pos Y: 8, cycle len L: 8
* Total Length is 10, when Y > 5 L goes wrong
cycle pos Y: 0, cycle len L: 10
cycle pos Y: 1, cycle len L: 9
cycle pos Y: 2, cycle len L: 8
cycle pos Y: 3, cycle len L: 7
cycle pos Y: 4, cycle len L: 6
cycle pos Y: 5, cycle len L: 5
cycle pos Y: 6, cycle len L: 8
cycle pos Y: 7, cycle len L: 9
cycle pos Y: 8, cycle len L: 8
cycle pos Y: 9, cycle len L: 9
* Total Length is 11, when Y > 5 L goes wrong
cycle pos Y: 0, cycle len L: 11
cycle pos Y: 1, cycle len L: 10
cycle pos Y: 2, cycle len L: 9
cycle pos Y: 3, cycle len L: 8
cycle pos Y: 4, cycle len L: 7
cycle pos Y: 5, cycle len L: 6
cycle pos Y: 6, cycle len L: 10
cycle pos Y: 7, cycle len L: 8
cycle pos Y: 8, cycle len L: 9
cycle pos Y: 9, cycle len L: 10
cycle pos Y: 10, cycle len L: 10
* Total Length is 12, when Y > 6 L goes wrong
cycle pos Y: 0, cycle len L: 12
cycle pos Y: 1, cycle len L: 11
cycle pos Y: 2, cycle len L: 10
cycle pos Y: 3, cycle len L: 9
cycle pos Y: 4, cycle len L: 8
cycle pos Y: 5, cycle len L: 7
cycle pos Y: 6, cycle len L: 6
cycle pos Y: 7, cycle len L: 10
cycle pos Y: 8, cycle len L: 8
cycle pos Y: 9, cycle len L: 9
cycle pos Y: 10, cycle len L: 10
cycle pos Y: 11, cycle len L: 11
* Total Length is 13, when Y > 6 L goes wrong
cycle pos Y: 0, cycle len L: 13
cycle pos Y: 1, cycle len L: 12
cycle pos Y: 2, cycle len L: 11
cycle pos Y: 3, cycle len L: 10
cycle pos Y: 4, cycle len L: 9
cycle pos Y: 5, cycle len L: 8
cycle pos Y: 6, cycle len L: 7
cycle pos Y: 7, cycle len L: 12
cycle pos Y: 8, cycle len L: 10
cycle pos Y: 9, cycle len L: 12
cycle pos Y: 10, cycle len L: 12
cycle pos Y: 11, cycle len L: 12
cycle pos Y: 12, cycle len L: 12
* Total Length is 14, when Y > 7 L goes wrong
cycle pos Y: 0, cycle len L: 14
cycle pos Y: 1, cycle len L: 13
cycle pos Y: 2, cycle len L: 12
cycle pos Y: 3, cycle len L: 11
cycle pos Y: 4, cycle len L: 10
cycle pos Y: 5, cycle len L: 9
cycle pos Y: 6, cycle len L: 8
cycle pos Y: 7, cycle len L: 7
cycle pos Y: 8, cycle len L: 12
cycle pos Y: 9, cycle len L: 10
cycle pos Y: 10, cycle len L: 12
cycle pos Y: 11, cycle len L: 12
cycle pos Y: 12, cycle len L: 12
cycle pos Y: 13, cycle len L: 13

 では、このバグを修正するにはどうすればよいでしょうか?

 

2. リングの位置はどうやって見つけますか?

すでに a=c という結論が得られているので、2 つのポインタをそれぞれ X と Z から開始し、一度に 1 ステップずつ実行すると、Y で合流します。つまり、リングの最初のノードです。

def checkclosedcycle(head):
    """检查当前链表是否存在闭环。
    无闭环返回False,否则返回发生闭环的位置。"""
    slow = fast = head
    exist = False
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow is fast and fast is not None:
            exist = True
            break
    if exist:  # 获取闭环位置
        seeker = self.head
        idx = 0
        while seeker is not slow:
            seeker = seeker.next
            slow = slow.next
            idx += 1
        return idx
    else:
        return False

3. リングを外すにはどうすればよいですか?

前の質問の最後で、セグメント c の点 Y の前のノードと Y の間のリンクを切断するだけです。

def checkclosedcycle(head, dismiss=False):
    """检查当前链表是否存在闭环,获取闭环的位置及长度。
    无闭环返回 (False, 0),否则返回 (闭环位置, 闭环长度)。
    若 dissmiss=True,则解除闭环。
    注意:解除闭环可能导致链表长度发生变化!"""
    slow = fast = head
    exist = False
    ciclen = 0
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        ciclen += 1
        if slow is fast and fast is not None:
            exist = True
            break
    if exist:  # 获取闭环位置
        seeker, end = self.head, slow
        idx = 0
        while seeker is not slow:
            seeker = seeker.next
            end = slow
            slow = slow.next
            idx += 1
        if dismiss:
            end.next = None
            # length = idx + ciclen # 链表长度发生变化
        return (idx, ciclen)
    else:
        return (False, 0)

4. 2 つの単一リンクリスト間に共通部分があるかどうかを判断するにはどうすればよいですか? 最初に交差するノードを見つけるにはどうすればよいでしょうか?

単一リンクされた 2 つのリストに共通部分があるかどうかを判断するにはどうすればよいですか? まず、2 つの連結リストに環があるかどうかを判断します。一方に環があり、もう一方に環がない場合は交差してはなりません。どちらも環を持たない場合は、2 つのリストの末尾が等しいかどうかを判断します。両方に環がある場合は、2 つのリストの末尾が等しいかどうかを判断します。 、リンク リスト上のZ 点が別のリンク リスト上にあるかどうかを判断します。

最初に交差するノードを見つけるにはどうすればよいでしょうか? 2 つのリンクされたリストの長さ L1 と L2 を求めます (リングがある場合は、点 Y を末尾ノードとして計算します)。L1<L2 を想定し、2 つのポインターを使用して 2 つのリンクされたリストの先頭からそれぞれ開始します。長さは L2 です。リンクされたリストが最初に (L2-L1) ステップ進み、次に 2 つが一致するまで一緒に進みます。

 

参考:

リンクされたリストに閉ループがあることを確認するにはどうすればよいですか?

[アルゴリズム][LeetCode]リンク リスト サイクル & リンク リスト サイクル II - 単一リンク リストのリング

おすすめ

転載: blog.csdn.net/liuqixuan1994/article/details/103790619