【LeetCode】Detailed explanation of circular linked list 141. Linked List Cycle Given a linked list, determine if it has a cycle in it. To


foreword

The first solution to this problem today is very strange. You can use the timer to AC, and you can adjust the time by yourself. Come and see it with me.


text

Original title:

Link: Circular linked list

Given a linked list, determine if it has a cycle in it.
To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to. If pos is -1, then there is no cycle in the linked list.
Example 1:
Input: head = [3,2,0,-4], pos = 1
Output: true
Explanation: There is a cycle in the linked list, where tail connects to the second node.
insert image description here
Example 2:
Input: head = [1,2], pos = 0
Output: true
Explanation: There is a cycle in the linked list, where tail connects to the first node.
insert image description here
Example 3:
Input: head = [1], pos = -1
Output: false
Explanation: There is no cycle in the linked list.
insert image description here
Follow up:
Can you solve it using O(1) (i.e. constant) memory?

The gist of the title
Given a linked list, determine whether it is a circular linked list, yes, it's that simple.

Idea 1:

Generally, when we traverse the linked list (non-circular linked list), we directly come up with a while (head != null), and then head = head.next in the loop body, similar to the following statement

while (head != null) {
    
    
       
    
	head = head.next;
}

However, the circular linked list will not work, and may encounter the problem of infinite loop leading to timeout. At this time, the first idea arises.
Since the circular linked list will time out, we limit the time to a certain range. For example, if it is still running after 0.1s, then there may be a circular linked list; you can use the System.currentTimeMillis() method that comes with Java to get the current time. , of course, other languages ​​also provide similar methods, and the code running time can be calculated by the following code:

// 过去的时间
long oldTime = System.currentTimeMillis();
while (true) {
    
    
       
    
	// 这里是循环体代码
}
// 当前时间
long curTime = System.currentTimeMillis();
// 代码运行时间等于curTime - oldTime

curTime - oldTime is the running time of the loop body, let's take a look at the complete code of idea 1:

code

public class Solution {
    
    
       
    
    public boolean hasCycle(ListNode head) {
    
    
       
    
        // 如果超过某个时间,比如0.1s,则说明有环
        long oldTime = System.currentTimeMillis();
        while (head != null) {
    
    
       
    
            head = head.next;
            long curTime = System.currentTimeMillis();
            if (curTime - oldTime > 100) {
    
    
       
    
                return true;
            }
        }
        return false;
    }
}

Submit the code, it is successful, but the time is too slow, we try to reduce the time difference to 100 by half.
insert image description here
When the time difference is changed to 50, it is found that AC can also be changed.
insert image description here
Continue to change it to 25, and the AC can still be
insert image description here
reduced by half, and 12 can be reduced by
insert image description here
two points, try Forget about 6
insert image description here
, just change it to 1 (curTime - oldTime > 1), you can still AC, but this time is really slow
( the time difference of the code is greater than 1, which is 1ms, the reason why the submission will become 17ms is because 17 test cases are provided, and each use case takes 1ms )
insert image description here
There is no need to continue later, let's change the way of thinking.

Idea 2

Since each node in the circular linked list has its own address, when we traverse the linked list, if there are duplicate addresses, it can indicate that there is a circular linked list.
The method of detecting repetition is also very simple. Use Set directly (Set is an unordered, non-repeatable data structure), traverse a node each time, and determine whether its address exists in the set. If it exists, it means that it is a circular linked list, otherwise Store it in a set.

code

public class Solution {
    
    
       
    
    public boolean hasCycle(ListNode head) {
    
    
       
    
        // 使用set存储hashcode
        Set<Integer> set = new HashSet<>();
        while (head != null) {
    
    
       
    
            if (set.contains(head.hashCode())) 
                return true;
            else 
                set.add(head.hashCode());
            head = head.next;
        }
        return false;
    }
}

Code explanation
Before storing the address, it is necessary to check whether it exists in the set, and it can be added to the set only if it does not exist;
after the entire loop is traversed, false is returned directly, because if there is a circular linked list, the if condition will definitely be satisfied, just directly Returns true, conversely, that is, there is no circular linked list, the loop ends, and returns false.
Submit the code, the time is slightly better, but the space is not enough. Due to the extra space, the complexity is O(N), and then we try to reduce its space to O(1).
insert image description here

Idea 3

I didn't think of this method, and I also got it from the data. The idea 3 is a bit similar to the tortoise and the hare race, assuming that the speed of the rabbit is 2 and the speed of the tortoise is 1. Of course, the rabbit is not sleeping, as shown in the figure below:
insert image description here
(The pattern is not the point )
The running trajectory of the two of them is like this, when the rabbit runs to 2, the tortoise climbs to 1; when the rabbit runs to 4, the tortoise climbs to 2... When the rabbit runs to 10, the tortoise climbs to 5, as shown in the figure below Note:
insert image description here
insert image description here
insert image description here
insert image description here
insert image description here
But if the track is changed, it will look like this:
insert image description here
At this time, as long as the hare and the tortoise are still running, they will definitely meet!
insert image description here
As shown in the picture above, the tortoise just happened to enter the small circle when the rabbit ran n circles, and the two of them met at this time.
We regard the line segment track at the beginning as an ordinary linked list, and the track with a small ring as a circular linked list. The condition for judging whether the circular linked list exists is to judge whether the tortoise and the rabbit meet , and the tortoise can use A slow pointer is represented, a rabbit is represented by a fast pointer, look at the code:

code

public class Solution {
    
    
       
    
    public boolean hasCycle(ListNode head) {
    
    
       
    
        // 快慢指针
        ListNode rabbit = head;
        ListNode tortoise = head;
        while (tortoise != null && rabbit != null && rabbit.next != null) {
    
    
       
    
            rabbit = rabbit.next.next;
            tortoise = tortoise.next;
            if (rabbit == tortoise) 
                return true;
        }
        return false;
    }
}

Code Explanation
At the beginning, both the rabbit variable and the turtle variable point to the head, that is, they all start running at the starting point.

ListNode rabbit = head;
ListNode tortoise = head;

Since the speed of the hare is 2, that is, in each loop, the hare = the next next to the hare; the speed of the turtle is 1, that is, in each loop, the turtle = the next one of the tortoise

rabbit = rabbit.next.next;
tortoise = tortoise.next;

Since the rabbit takes 2 steps each time, in the while, in addition to making the node of the current rabbit not empty, the next one of the current rabbit should not be empty, this is to avoid the occurrence of a null pointer.

while (tortoise != null && rabbit != null && rabbit.next != null) 

When the two meet, that is, rabbit == tortoise, return true to indicate that there is a circular linked list, submit the code, perfect!
insert image description here
Some people may have doubts, does the speed of the rabbit have to be 2? In fact, it is not necessarily. The rabbit is 2 and the turtle is 1 just to meet faster. Of course, you can also change it to 3, 4, and 5, but the loop conditions in the while should also be changed, which is a little more troublesome.


Summarize

After many people finish an algorithm problem, they won't do it next time they take it out. I also encountered this problem. Later, I found that if you have mastered multiple solutions to an algorithm problem, and occasionally come back to review it, you will not be able to do it. It's so easy to forget, which is why I write blogs like this: why not do it while consolidating my own memories while helping others?

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325982654&siteId=291194637