Four Array Double Pointer and Slide List
Images Source: https://realpython.com/
Datewhale31_u LeetCode Title:
Route pioneer: Yang Shichao
Navigator: Liu Jun
Sailors: Yang Shichao, Li Yanpeng, Ye Zhixiong, Zhao Zi
Open Source Content
Open source e-books
1 Array Double Pointer
Two Pointers: refers to the process of traversing elements, not using a single pointer for access, but using two pointers for access to achieve the appropriate purpose. If the two pointers are in the opposite direction, they are called "colliding clocks". If the two pointers have the same direction, they are called "fast and slow pointers". If two pointers belong to different arrays/linked lists, they are called Separate Double Pointers.
- The time complexity of an array problem is usually O ( n 2 ) O(n^2) O(n2) - >> to O(n) using interval monotonicity >>
1.2 Collision Pointer
Collision pointer: Two pointers, left and right, point to the first and last element in a sequence, and then the left pointer increases and the right pointer decreases until the values of the two pointers collide (that is, left == right), or until special conditions satisfy other requirements.
(1) Two pointers left, right, left pointing to the first element. Right pointing to the last element;
(2) Close to each other: left += 1, right -= 1
(3) Collision: left == right or other condition jump out of condition
- Commonly used to solve ordered array or string problems
1.2.1 Questions 125. Verify palindrome string
- Think: Same on left and right > > > Both sides begin to traverse contrast > > Collision
class Solution: def isPalindrome(self, s: str) -> bool: left, right = 0, len(s) - 1 while left < right: if not s[left].isalnum(): #Determine whether left and right are letters left += 1 continue if not s[right].isalnum(): right -= 1 continue if s[left].lower() != s[right].lower(): return False else: right -= 1 left += 1 return True
1.2.2 Questions 11.Container for maximum water-LeetCode
- Thought: Water accumulation = left and right smaller height * width > >> width decreases with left and right movement. The smaller the height, the larger the area >>> which is smaller moves inward
class Solution: def maxArea(self, height: List[int]) -> int: left, right, ans = 0, len(height) - 1, 0 while left < right: ans = max(ans, min(height[left], height[right])*(right - left)) if height[left] < height[right]: left += 1 else: right -= 1 return ans
1.3 Collision Pointer
Fast or slow pointer: Two pointers start traversing a sequence from the same side and move one fast or one slow step at a time. A fast-moving pointer is called a fast pointer, and a slow-moving pointer is called a slow pointer. The two pointers move at different speeds and strategies until the fast pointer moves to the end of the array, or when the two pointers intersect, or when other special conditions are met.
(1) Two pointers slow, fast, generally slow = 0, fast = 1
(2) Two pointers move conditionally
(3) Quick pointer to the head or other conditions to jump out
Usually used to handle moving or deleting elements in an array, or to determine if there is a ring or length problem in a chain table
1.3.1 Questions 26. Delete duplicates in an ordered array - LeetCode
- Idea: Make slow, fast = 0, 1, slow not equal to fast >> Make the element that fast points to the position copy to the slow position, slow move backward by one >> until the fast traversal is complete
class Solution: def removeDuplicates(self, nums: List[int]) -> int: if len(nums) <= 1: return len(nums) slow, fast = 0, 1 while (fast < len(nums)): if nums[slow] != nums[fast]: slow += 1 nums[slow] = nums[fast] fast += 1 return slow + 1
1.4 Separate double pointer
Separate double pointer: Two pointers belong to different arrays/linked lists, and two pointers move in two arrays/linked lists.
(1) Two pointers left_1. left_2, pointing to two arrays, the first element of the chain table
(2) satisfying the condition left_1. left_2 Move right at the same time, or only one of them
(3) One of the arrays or lists has been traversed or other conditions have met the jump
Generally used to handle combinatorial union of ordinal numbers, intersection, union problems
1.4.1 Questions 349. Intersection of two arrays
- Idea: Traverse through the two arrays after sorting, add res >>> if they are equal, and move the pointer one bit to the right if the array element is small
class Solution: def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: nums1.sort() nums2.sort() left_1 = 0 left_2 = 0 res =  while left_1 < len(nums1) and left_2 < len(nums2): if nums1[left_1] == nums2[left_2]: if nums1[left_1] not in res: res.append(nums1[left_1]) left_1 += 1 left_2 += 1 elif nums1[left_1] < nums2[left_2]: left_1 += 1 elif nums1[left_1] > nums2[left_2]: left_2 += 1 return res
1.5 Double Pointer Related Topics
1.5.1 344. Reverse string
- You can't use extra space >>> double pointer to traverse a thought topic, swapping its corresponding values in place
class Solution: def reverseString(self, s: List[str]) -> None: """ Do not return anything, modify s in-place instead. """ left, right = 0, len(s) - 1 while left < right: s[left], s[right] = s[right], s[left]344 . strrev left += 1 right -= 1
1.5.2 15.Sum of Three
- Method 1 ------------- for traverses an element, and the remaining space uses the double pointer to traverse to get the value of the element >> Then consider how to move the pointer left and right.
class Solution: def threeSum(self, nums: List[int]) -> List[List[int]]: nums.sort() ans =  n = len(nums) for i in range(n): if i > 0 and nums[i] == nums[i - 1]: # If i is the same as the previous number, go directly to the next i continue left = i + 1 right = n - 1 while left < right: while left < right and left > i + 1 and nums[left] == nums[left - 1]: #Prevent left pointer from pointing to element repeatedly left += 1 while left < right and right < n - 1 and nums[right] == nums[right + 1]: right -= 1 if left < right and nums[i] + nums[left] + nums[right] == 0: ans.append([nums[i], nums[left], nums[right]]) left += 1 right -= 1 elif nums[i] + nums[left] + nums[right] > 0: right -= 1 else: left += 1 return ans
- Thought: Two fast and slow pointers start at the same time, the first two elements must be in the answer >>> Consider whether the element placed on top of slow is equal to fast >> When the two elements are equal, there are: the last element on slow== the last element on slow== the corresponding element of fast; The fast corresponding element is not preserved at this time
class Solution: def removeDuplicates(self, nums: List[int]) -> int: size = len(nums) if size <= 2: return size slow, fast = 2, 2 #Starting from the third element, the first two pointer finger elements are guaranteed to exist while (fast < size): if nums[slow - 2] != nums[fast]: nums[slow] = nums[fast] slow += 1 fast += 1 return slow
1.5.4 283. Move zero
- Fa I--------- Double Pointer
class Solution: def moveZeroes(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ p1, p2 = 0, 0 while p2 < len(nums): if nums[p2] != 0: nums[p2], nums[p1] = nums[p1], nums[p2] p1 += 1 p2 += 1
- Fa 2 - - A pointer traverses non-0 + fills 0
count = 0 for i in range(len(nums)): if nums[i] != 0: nums[count] = nums[i] count += 1 while count < len(nums): nums[count] = 0 count += 1
- Important: Combine nums2 to nums1 in a non-decreasing order; The merge is stored in the array nums1; Time complexity is O(m + n)
- Note: Use double pointer Both pointers end at the end and need extra space because elements in nums1 may be overwritten before being removed if merged directly into the array nums1. Therefore, an inverted double pointer is introduced, since there are n zeros behind num1 to store ifqualified values that will not be overwritten:
class Solution: def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: """ Do not return anything, modify nums1 in-place instead. """ count_1 = m-1 count_2 = n-1 for p in range(m + n-1, -1, -1): #Reverse nums1[:] and prepare filtered values if count_2 < 0 or (nums1[count_1] >= nums2[count_2] and count_1 >= 0 ): # Select nums1 elements when a large number of arrays or nums2 arrays are bounded by if filtering nums1[p] = nums1[count_1] count_1 -= 1 else: nums1[p] = nums2[count_2] count_2 -= 1
1.5.6 75. Color Classification
- Idea Dual Pointer Finger >>> Move causes the final left to point to the tail of 0. right points to the beginning of 2 >> Left encounters 0. right encounters 2 and exchanges its corresponding elements >>> Note that when left exceeds or encounters 1_right Move
class Solution: def sortColors(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ n = len(nums) left = 0 #The left pointer eventually points to the end of 0 right = n-1 #The right pointer ends at the beginning of 2 i = 0 while i <= right: if i < left: # Left is processed data i less than left needs to be moved right i += 1 elif nums[i] == 0: nums[i],nums[left] = nums[left],nums[i] left += 1 elif nums[i] == 2: nums[i],nums[right] = nums[right],nums[i] right -= 1 else: #Move right when nums[i] is 1 i += 1
2 Slide Window
Sliding Window: Maintains a fixed or variable length window on a given array/string. You can slide windows, zoom in and out, and maintain optimal solutions.
- Slide operation: The window can move in a certain direction. The most common move is to the right.
- Zoom operation: For windows of variable length, you can reduce the window length from the left or increase the window length from the right.
Sliding window algorithms are generally used to solve some problems in finding the properties (length, etc.) of continuous intervals that meet certain conditions. The algorithm can turn a nested loop in some problems into a single loop, so it can reduce time complexity.
2.2 Related Examples
2.2.1 Fixed Length- 1343. Number of subarrays with size K and mean greater than or equal to the threshold
- Ideas: Use left and right to maintain a window >>> when the window is K >> according to whether the mean value meets the criteria >> right, and move the left to the right to ensure the window width
class Solution: def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int: left = 0 right = 0 window_sum = 0 ans = 0 while right < len(arr): window_sum += arr[right] if right - left + 1 >= k: if window_sum >= k * threshold: ans += 1 window_sum -= arr[left] left += 1 right += 1 return ans
2.2.2 Indefinite Length- 3. LeetCode, the longest substring without repeating characters
Both left and right point to 0.
Add the rightmost character s[right] to the current window to record the number of characters.
If there is more than one character in the window, that is window[s[right] > 1, keep moving left, reduce the length of the sliding window, and update the number of corresponding characters in the window until window[s[right] <= 1.
Maintains the maximum substring length for updates with no duplicate characters. Then move right until right >= len (nums) ends.
The longest substring length for output without duplicate characters.
class Solution: def lengthOfLongestSubstring(self, s: str) -> int: left = 0 right = 0 window = dict() ans = 0 while right < len(s): if s[right] not in window: window[s[right]] = 1 else: window[s[right]] += 1 while window[s[right]] > 1: window[s[left]] -= 1 left += 1 ans = max(ans, right - left + 1) right += 1 return ans
- https://www.cnblogs.com/kyoner/p/11087755.html Summary of Dual Pointer Skills