2021 Autumn trick-algorithm-summary of double pointer skills

Summary of Algorithms-Dual Pointer Skills

Lark Summary Frame - Double Pointer Skill Summary - Reproduction

Same as above - better typesetting
Ibid. - Further on the subject

I think the dual pointer technique can also be divided into two categories, one is the "fast and slow pointer" and the other is the "left and right pointer".The former mainly solves problems in the list of chains, such as determining whether or not a ring is included in a typical list of chains, while the latter mainly solves problems in arrays (or strings), such as bipartition lookup.

1. Common algorithms for fast and slow pointers

In the fast and slow pointers, both the fast pointer and the slow pointer initialize the head node pointing to the chain table. The fast pointer fast is in the front while the slow pointer is in the back, which cleverly solves some problems in the chain table.

1.1 Determine if a chain list contains a ring-leetcode-141.Ring chain list-Simple

141. Ring Chain List
 Given a list of chains, determine if there are rings in the list.
To represent rings in a given chain table, we use the integer pos to represent where the end of the chain is connected to the chain table (the index starts at 0). 
If pos is -1, there are no rings in the list.

Example 1:
Input: head = [3,2,0,-4], pos = 1
 Output:true
 Interpretation: There is a ring in the chain table whose tail is connected to the second node.

This should be the most basic operation of a chain table and can be skipped if the reader already knows this technique.

A single-chain list is characterized by that each node only knows the next node, so it is impossible to tell if a pointer has a ring in the list.

If there are no rings in the list, the pointer will eventually encounter a null pointer indicating the end of the list, which is good to say, can tell that the list does not contain rings.

boolean hasCycle(ListNode head) {
    while (head != null)
        head = head.next;
    return false;
}

However, if the list contains rings, the pointer will fall into an infinite loop because there is no null pointer in the ring array as the tail node.

The classical solution is to use two pointers, one to run fast and one to run slowly.If there are no rings, the fast pointer will eventually encounter null, indicating that the chain table does not contain rings; if there are rings, the fast pointer will eventually make a super slow pointer circle, encountering the slow pointer, indicating that the chain table contains rings.

boolean hasCycle(ListNode head) {
    ListNode fast, slow;
    fast = slow = head;
    //Pay attention to this cycle condition!!!
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;

        if (fast == slow) return true;
    }
    return false;
}
# python -AC 
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        fastPointer = slowPointer = head
        while fastPointer and fastPointer.next:
            fastPointer = fastPointer.next.next
            slowPointer = slowPointer.next
            
            if fastPointer == slowPointer:
                return True
        
        return False

Notice the above loop condition, it avoids some null value problems of the fast pointer!!!

1.2 Known chain list contains rings, return the starting position of this ring - 142. Ring list II - Medium

142. Ring Chain List II
 Given a list of chains, returns the first node where the list of chains begins to ring.If the list of chains is not looped, null is returned.
To represent rings in a given chain table, we use the integer pos to represent where the end of the chain is connected to the chain table (the index starts at 0).
 If pos is -1, there are no rings in the list.
Description: Modifying a given list of chains is not allowed.

Example 1:
Input: head = [3,2,0,-4], pos = 1
 Output: tail connects to node index 1
 Interpretation: There is a ring in the chain table whose tail is connected to the second node.


It's not difficult at all. It's a bit like a brain tear. Just look at the code first:

ListNode detectCycle(ListNode head) {
    ListNode fast, slow;
    fast = slow = head;
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow) break;
    }
    // The code above looks like a hasCycle function
    slow = head;
    while (slow != fast) {
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
}

python-AC code

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        # Thought fast slow pointer, find the ring and walk together, meet is the ring entrance
        fastPointer = slowPointer = head
        flag = 0 
        while fastPointer and fastPointer.next:
            fastPointer = fastPointer.next.next
            slowPointer = slowPointer.next
            
            if fastPointer == slowPointer:
                flag = 1
                break
        if flag == 0:
            return None
        slowPointer = head
        # Resynchronize walk
        while slowPointer != fastPointer:
            fastPointer = fastPointer.next
            slowPointer = slowPointer.next
            
        return slowPointer

You can see that when the fast and slow pointers meet, let either pointer point to the head node, and then let them go forward at the same speed. When they meet again, the node location is where the ring begins.Why is that?

The first time you meet, assume that the slow pointer has taken k steps, then the fast pointer must have taken 2 k steps, that is, k steps more than slow.

The specific mathematical proof is omitted here;


If the distance between the meeting point and the starting point of the ring is m, then the distance between the starting point of the ring and the head of the head node is k - m, that is, if K - M steps are taken from the head, the starting point of the ring can be reached.

Coincidentally, if you continue k - m steps from the point of encounter, you will also reach the beginning of the ring.

So, as long as we re-point either of the fast or slow pointers to the head, and then the two pointers move forward at the same speed, the k - m steps will meet, and where they meet is the starting point of the ring.

⭐1.3 Find Midpoint of Chain List - Merge and Sort of Chain List

Similar to the above idea, we can also make the fast pointer go forward two steps at a time and the slow pointer go forward one step at a time. When the fast pointer reaches the end of the chain table, the slow pointer is in the middle of the chain table.

while (fast != null && fast.next != null) {
    fast = fast.next.next;
    slow = slow.next;
}
// slow is in the middle
return slow;

When the length of the list is odd, slow happens to stop at the midpoint; if the length is even, slow ends at the middle right:

An important role in finding the midpoint of a chain table is to merge and sort the list of chains.

Recall merge sort of arrays: find the midpoint index to recursively divide the array, and merge the two ordered arrays.For a chain table, merging two ordered lists is very simple, the difficulty lies in dichotomy.

But now that you have learned to find the midpoint of the list, you can divide the list.The specific content about merge sort is not expanded in this paper.

Tags: Python

Posted on Tue, 23 Jun 2020 12:19:04 -0400 by webbrowser