[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!