Algorithm of double pointer problem

Double pointers are mainly divided into two categories: fast and slow pointers and left and right pointers

Speed pointer

For the linked list problem, we can generally use the speed pointer to solve it
The so-called speed pointer refers to using two pointers to advance at different speeds. There are two pointers that we can determine:

  1. Is there a link in the linked list
  2. The penultimate node of the lin k ed list

Some topics

For example: Circular linked list of 141 questions

Note: when two pointers meet, there is a ring (similar to running on the playground, if the speed is not equal, they can always meet)

code

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        if not head:
            return False
        fast_node = head.next
        slow_node = head
        while fast_node and fast_node.next:
            if slow_node == fast_node:
                return True
            fast_node = fast_node.next.next
            slow_node = slow_node.next
        return False

Another example:

Left and right pointer

The general form of left and right pointers is what we often call binary search (binary search)

General usage:

def binary_search(nums: List[int], target: int):
    left = 0
    right = ...
    while ...:
        mid = left + (right - left) / 2
        if nums[mid] == target:
            ...
        elif nums[mid] < target:
            left = ...
        elif nums[mid] > target:
            right = ...
            
    return ...

What we need to pay attention to is part:

  1. The first two Yes, it is determined by the search interval, that is, the problem is [left: right] or [left: right]
    [left: right]: right = len(nums) - 1 and while left < = right
    [left: right): right = len(nums) and while left < right
    The end condition of the former is: [right + 1: right] (for example: [3: 2])
    The end condition of the latter is: [left: right] (for example: [2: 2])
  2. hinder... Determined by the subject
    nums[mid] == target: the problem of returning a certain value is returned directly; Return to the problem of boundary, narrow the boundary (right = mid or left = mid)
    Num [mid] > target: too large, usually to narrow the right boundary: right = mid - 1
    Num [mid] < target: too small, usually to narrow the left boundary: left = mid + 1

For some problems, we can unify the interval of the problem as [left: right], that is, right = len (Num) - 1 and while left < = right

For example:
Problem of returning a value

def binary_search(nums: List[int], target: int):
    left = 0
    right = len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] < target:
            left = mid + 1
        elif nums[mid] > target:
            right = mid - 1
        elif nums[mid] == target:
            # Direct return
            return mid
    return -1

Corresponding questions on leetcode: 704. Binary search (simple) Problem solution

Problem of returning to the left boundary

def left_bound(nums: List[int], target: int):
    left = 0
    right = len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] < target:
            left = mid + 1
        elif nums[mid] > target:
            right = mid - 1
        elif nums[mid] == target:
            # Don't go back, lock the left boundary
            right = mid - 1

    # Finally, check the left crossing
    # left may be out of range, i.e. > = len (nums)
    if left >= len(nums) or nums[left] != target:
        return -1
    return left

The end condition is: [right + 1: right] (for example: [3: 2])
Return left border

Problem of returning to the right boundary

def right_bound(nums: List[int], target: int):
    left = 0
    right = len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] < target:
            left = mid + 1
        elif nums[mid] > target:
            right = mid - 1
        elif nums[mid] == target:
            # Don't go back, lock the right boundary
            left = mid + 1

    # Finally, check the right crossing
    if right < 0 or nums[right] != target:
        return -1

    return right

The end condition is: [right + 1: right] (for example: [3: 2])
Return to right boundary

Corresponding questions on leetcode:

sliding window

Sliding window is a kind of double pointer, which can also be said to be a kind of left and right pointers
It mainly solves the substring problem
The so-called substring problem refers to whether a long string contains a short string (including quantity)
Idea of sliding window:
① We can set two pointers corresponding to the left and right boundaries of the window
② The right boundary expands the window to the right until a qualified substring is found
③ Narrow the window from the left border to the right, update the data, and return according to the title
④ If not, repeat ② and ③
⑤ Whether to return according to the topic

Template:

def sliding_window(s: str, t: str):
    """
    :param s Large string
    :param t Small string
    :return Return according to the topic
    
    """
    need = {}  # Number of characters required for storage
    windows = {}  # Number of characters stored in the current window
    # Record the number of characters required
    for char in t:
        # The number of characters required increases by 1
        need[char] = need.setdefault(char, 0) + 1  # setdefault returns 0 when there are no characters in the dictionary
    # Pointer to left and right boundary
    left = right = 0
    # Used to record the number of characters that have passed the inspection
    valid = 0
    """
    If there are other variables, you can add them here
    """
    while right < len(s):
        # right_char is the character of the window to be moved in
        right_char = s[right]
        # Move window right
        right += 1
        # If the character of the current right boundary is the required character
        if right_char in need:
            """
            Update data
            Generally
            1. Data of current window
            2. Number of characters passed
            """
            # Record the number of characters that meet the requirements of the current window
            windows[right_char] = windows.setdefault(right_char, 0) + 1
            # If the current character already meets the requirements
            # Number of characters passed + 1
            if windows[right_char] == need[right_char]:
                valid += 1
        
        # Determine whether the left boundary can shrink
        # !!!!  This condition varies according to the subject
        while condition:
            """
            According to whether the title is returned here
            """
            
            # left_char is the character of the window to be moved out
            left_char = s[left]
            left += 1
            if left_char in need:
                """"
                Update data
                Generally
                1. Data of current window
                2. Number of characters passed
                
                Other customized variables are also updated here
                """
                # If the current character already meets the requirements
                # Number of characters passed - 1
                if windows[left_char] == need[left_char]:
                    valid -= 1
                # Record the number of characters that meet the requirements of the current window - 1
                windows[left_char] = windows.setdefault(left_char, 0) - 1
    """
    According to whether the title is returned here
    """

The main points to note about this template are

  1. Do you need some other variables, such as the length of the current substring
  2. Conditions for boundary reduction
  3. When will you return

Some topics

Keywords: data structure

Added by dreado on Tue, 01 Feb 2022 17:53:13 +0200