[algorithm] [speed pointer] intersection of circular linked list -- Explanation of two classic topics of force deduction [super detailed nanny level tutorial]

[algorithm] [speed pointer] intersection of circular linked list - explanation of two classic topics of force deduction [super detailed nanny level tutorial]
It's not easy to like first and then look good. It's all done with great care. I hope to get support. Your praise and support is a very important motivation for me. Don't forget to pay attention to me after reading it! ️️️

This article is full of work -- it is recommended to eat it after collection

[example 1] force buckle 141. Ring linked list

subject


Algorithm and idea

This problem is actually very simple to write: we can solve it with a double pointer. We can define a fast pointer and a slow pointer. The fast pointer takes two steps at a time and the slow pointer takes one step at a time.
Obviously, the fast pointer goes ahead of the loop, and the slow pointer goes after the loop, because after entering the loop, the fast pointer will always catch up with the slow pointer. If there is no loop, the tail of fast and slow will not be equal.
Obviously, we have worked out this problem, but is it really that simple? In fact, there are still some principles we need to explore.

If we are in the interview, the interviewer asks us to write the code of this problem. I believe we can do this problem in a minute.
If the interviewer asks us to prove why fast takes two steps at a time and slow takes one step at a time? How should we deal with it?
If fast takes three steps at a time and four steps at a time, can't it?
The answer is No.
Imagine that fast and slow have entered the loop and fast is going to catch up with slow. If we first follow our original idea, fast takes two steps at a time and slow takes one step at a time, then the distance between fast and slow decreases by 1 every time. This is very critical, which ensures that fast and slow will not miss each other.
If our fast takes three steps at a time and slow takes one step at a time, in some cases, fast will miss slow, which will lead to problems. Therefore, we must ensure that the speed difference between fast and slow can only be 1.

code implementation

bool hasCycle(struct ListNode* head) {
    struct ListNode* slow = head;//Slow pointer
    struct ListNode* fast = head;//Quick pointer
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
        //After entering the loop, the fast pointer will be caught up by the slow pointer sooner or later
        if (slow == fast) {
            return true;
        }
    }
    return false;//If there is a loop, it indicates that NULL is encountered, that is, there is no ring.
}

[example 2] force buckle 142. Ring linked list ll

subject



This problem looks similar to the previous one, but its implementation is still more complex

Here I provide two ideas for your reference

Idea 1:

The idea is relatively simple, but the code is relatively long.

Implementation process:
At the beginning, similar to the previous question, we first use the fast and slow pointer. When the fast pointer and the slow pointer are equal, record the position and disconnect. As shown in the figure:
In this way, we get two intersecting linked lists.
We turn this problem into the problem of finding intersection points in the intersecting linked list.
I won't repeat the method of finding intersection points in the intersecting linked list here, which doesn't belong to the problem of circular linked list.
If you have questions about finding intersections in the intersecting linked list, you can confide in me. If more people ask this question, I will write a blog about the intersection of the ring linked list for everyone to eat.

Idea 2:

The thought proof process of this idea is more complex, but the code is very concise.

Analysis process:
Train of thought 2: it is simple to write, requires formula proof, and complex to understand
Suppose: the distance between head - > POS is L
The intersection of pos - > fast & & slow is X
The circumference of the ring is C

When we met:
Slow pointer: L+X, ---- a partner asked: is it possible to meet only after setting a circle? It's impossible, because the speed is only 1
At this time, many partners will say: the pointer is going: L+X+C – > this conclusion can be deduced. One pointer starts from the meeting point and the other pointer starts from the head and will meet at the pos. As shown in the figure:

Although the conclusion is correct, the derivation is wrong. Why? If the distance before entering the ring is very long, the fast pointer will go more than one circle and the slow pointer will come in. Therefore – > the correct writing is
Let's go: L+X+nC
So we get: L=n*C-X
Before that, fast may have gone n laps
This is the correct derivation.
But we can find that we get the same conclusion, because head1 meets head2 after n laps.

Idea 1 code implementation:

struct ListNode* detectCycle(struct ListNode* head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    //The meeting of fast and slow pointers
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) {
            break;
        }
    }
//When you come out, you must judge the cycle that jumps out because there is no ring
//Or did you jump out of the loop because you met
    if (head == NULL || head->next == NULL) {//If only one node is given, the following judgment condition is not enough.
        return NULL;
    }
    if (fast != slow) {
        return NULL;
    }

    //Disconnect front and rear
    struct ListNode* head2 = fast->next;
    fast->next = NULL;

    //It is transformed into a linked list intersection problem
    struct ListNode* head1 = head;
    struct ListNode* cur1 = head1;
    struct ListNode* cur2 = head2;
    int l1 = 0;
    int l2 = 0;
    while (cur1) {
        l1++;
        cur1 = cur1->next;
    }
    while (cur2) {
        l2++;
        cur2 = cur2->next;
    }
    if (l2 > l1) {
        for (int i = 0; i < l2 - l1; i++) {
            head2 = head2->next;
        }
    }
    else {
        for (int i = 0; i < l1 - l2; i++) {
            head1 = head1->next;
        }
    }
    while (head1) {
        if (head1 == head2) {
            break;
        }
        head1 = head1->next;
        head2 = head2->next;
    }
    return head1;
}

Idea 2 code implementation:

struct ListNode* detectCycle(struct ListNode* head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    //Fast and slow pointer
    while (fast && fast->next) {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) {
            break;
        }
    }
    //If there is only one node, it can't be a ring
    if (head == NULL || head->next == NULL) {
        return NULL;
    }//Directly return null
    if (fast != slow) {//If there is only one node, it will also be satisfied here!
        return NULL;
    }
    //Find loop point
    struct ListNode* cur1 = head;
    struct ListNode* cur2 = fast;
    while (cur1 != cur2) {
        cur1 = cur1->next;
        cur2 = cur2->next;
    }
    return cur1;
}

Epilogue

The above is the whole content of the two classic topics and analysis of today's circular linked list. I hope all my friends can gain something after reading it. If you still don't understand some details in the code, you can confide in me.
Don't forget to order a favorite before you leave!

Tags: Algorithm data structure leetcode linked list

Posted on Sun, 05 Dec 2021 07:48:49 -0500 by rakhi