[Mistake Correction] How to determine if there is a closed loop in a linked list and where the closed loop occurs?

Use two pointers p1, p2 (increase 1 and 2 each time) to judge

Use two pointers: slow and fast, slow moves one bit at a time, fast moves two bits at a time, ends when one of the following conditions occurs, and the time complexity is O(n).

  • The first condition for termination is that the pointer p2 encounters a NULL node. This means that there is no closed loop
  • Another conditional expression terminates when the two pointers meet, which means that there is a closed loop

Why do the two must meet when there is a ring? Because fast enters the ring first, after slow enters, if slow is regarded as the front, fast will move closer to slow by 1 every time in the next cycle, so they will definitely meet, and fast will not directly skip slow.

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

extended question

LeetCode Questions

Linked List Cycle

Given a linked list, determine if it has a cycle in it.

Follow up: Can you solve it without using extra space?

How to check if there is a cycle in a singly linked list?

Linked List Cycle II

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Follow up: Can you solve it without using extra space?

How to find the first node of the ring?

I collected some questions related to this problem on the Internet, and my thinking has broadened a lot. The summary is as follows:

1. What is the length of the ring?

2. How to find the first node in the ring (ie Linked List Cycle II)?

3. How to turn a linked list with a ring into a singly linked list (remove the ring)?

4. How to judge whether there is an intersection between two singly linked lists? How to find the first intersecting node?

First, let's look at the following picture:

Suppose: the head of the linked list is X , the first node of the ring is Y , and the first intersection of slow and fast is Z. The lengths of each segment are a, b, c respectively , as shown in the figure. The length of the loop is L. The speeds of slow and fast are qs, qf respectively.

Let's analyze each problem one by one.

1. What is the length of the ring?

Method 1 (this answer is all over the Internet):

After the first encounter, let slow, fast continue to go, and record how many times it cycles until the next encounter. Because when fast reaches point Z for the second time, fast walks one circle, slow walks half a circle, and when fast reaches point Z for the third time, fast walks two circles, slow walks one circle, just in Z Point to meet.

Method Two:

After the first encounter, let fast stop, slow continue to walk, and record how many times it cycles until the next encounter.

Method 3 (simplest): The distance traveled by slow when meeting for the first time: a+b, the distance traveled by fast: a+b+c+b.

Because the speed of fast is twice that of slow, the distance traveled by fast is twice that of slow. There is 2(a+b) = a+b+c+b, and a=c can be obtained (this conclusion is very important! ) .

We find that L=b+c=a+b , that is, the number of cycles from the beginning to the first encounter of the two is equal to the length of the ring.

! ! ! Note: I actually tested method 3 and found that there is actually a bug. Here, the original author defaults that when slow and fast meet for the first time, fast only takes one more lap than slow. However, there are exceptions in practice, namely: when a>b+ At c, when slow reaches point Y, fast has already gone 2a>2(b+c), that is, it has gone through more than two laps. At this time, even if fast and slow meet within one lap, the above inference a=c is also Can't be true, so something is bound to go wrong!

See the code follow-up for the experimental results.

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

 In response to the bug I mentioned, the following experimental code is designed. It can be seen from the results that when the position of Y exceeds 1/2 Total, the result will always be wrong.

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()

The result is as follows: 

* 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

 So, how to fix this bug?

 

2. How to find the position of the ring?

We have already got the conclusion a=c, then let the two pointers start from X and Z respectively, one step at a time, then they will meet at Y! That is, the first node of the ring.

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. How to remove the ring?

At the end of the previous question, just cut off the link between the node before point Y in segment c and 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. How to judge whether there is an intersection between two singly linked lists? How to find the first intersecting node?

How to judge whether two singly linked lists have an intersection? First judge whether the two linked lists have rings. If one has a ring and the other has no rings, they must not intersect; if neither of them has a ring, judge whether the tails of the two lists are equal; if both have rings, judge the Z on a linked list Whether the point is on another linked list.

How to find the first intersecting node? Find the lengths L1 and L2 of the two linked lists (if there is a ring, take point Y as the tail node to calculate), assuming L1<L2, use two pointers to start from the head of the two linked lists respectively, and the length is L2 The linked list goes (L2-L1) steps first, and then the two go together until they meet.

 

reference:

How to determine that there is a closed loop in a linked list?

[Algorithm][LeetCode]Linked List Cycle & Linked List Cycle II - the ring in the singly linked list

Guess you like

Origin blog.csdn.net/liuqixuan1994/article/details/103790619