Two Pointers

Lead of Two Pointers

Two pointers to swap

Below is a template to use two pointers to swap elements of a list s. One pointer p1 starts at the beginning of s and the other pointer p2 starts at the end of s. As long as p1 < p2, we swap the two elements pointed by these two pointers, increase p1 and decrease p2. We keep doing this until p1 >= p2.

 1def templateTwoPointersToSwap(s: List[str]) -> None:
 2    p1 = 0
 3    p2 = len(s) - 1
 4    while True:
 5        if p1 < p2:
 6            s[p1], s[p2] = s[p2], s[p1]
 7            p1 += 1
 8            p2 -= 1
 9        else:
10            break

We can defintely replace while True: with while p1 < p2: and avoid using the if condition. However, we intentionally write the template in such a way to make it consistent with the other templates for two pointers. Some problems that can be solved by this template, as well as their solutions, are as follows.

LeetCode 344. Reverse String (Easy)

Its solution shown below is a very straightforward application of the template.

 1class Solution:
 2    def reverseString(self, s: List[str]) -> None:
 3        p1 = 0
 4        p2 = len(s) - 1
 5        while True:
 6            if p1 < p2:
 7                s[p1], s[p2] = s[p2], s[p1]
 8                p1 += 1
 9                p2 -= 1
10            else:
11                break

LeetCode 7. Reverse Integer (Medium)

We just need to convert an integer to a list of strings. Then the problem is converted to LeetCode 344. Reverse String (Easy).

 1class Solution:
 2    def reverse(self, x: int) -> int:
 3        sign = -1 if x < 0 else 1
 4        s = list(str(abs(x)))
 5        
 6        p1 = 0
 7        p2 = len(s) - 1
 8        while True:
 9            if p1 < p2:
10                s[p1], s[p2] = s[p2], s[p1]
11                p1 += 1
12                p2 -= 1
13            else:
14                break
15            
16        x = int(''.join(s)) * sign
17        return x if -2 ** 31 <= x <= 2 ** 31 - 1 else 0

LeetCode 31. Next Permutation (Medium)

The idea is to find the “first drop” when traversing from right to left, replace the “first drop” with the smallest element that is greater than it and to its right, and reverse all elements to its right. More specifically, we need 4 steps (take nums = [8, 7, 4, 2, 6, 5, 3, 1] as an example):

  • Traverse the list from right to left, and stop as soon as we encounter the first drop / decrease (the number 2 at index 3). Let i1 - 1 be its index (i1 - 1 = 3)

    • If the list nums is sorted descendingly and we can’t find such a drop, the next permutation is just sorted(nums)
  • Among the elements to the right of nums[i1 - 1], find the smallest / rightmost element that is greater than nums[i1 - 1] (the number 3 at index 6). Let i2 - 1 be its index (i2 - 1 = 6)

  • Swap nums[i1 - 1] and nums[i2 - 1] (nums = [8, 7, 4, 3, 6, 5, 2, 1])

  • Reverse all the elements to the right of nums[i1 - 1] using our template to get the final result (nums = [8, 7, 4, 3, 1, 2, 5, 6])

 1class Solution:
 2    def nextPermutation(self, nums: List[int]) -> None:
 3        """
 4        Do not return anything, modify nums in-place instead.
 5        """
 6        i1 = len(nums) - 1
 7        while i1 > 0 and nums[i1 - 1] >= nums[i1]:
 8            i1 -= 1
 9            
10        if i1 == 0:
11            nums.sort()
12            return
13        
14        i2 = i1
15        while i2 < len(nums) and nums[i2] > nums[i1 - 1]:
16            i2 += 1
17            
18        nums[i1 - 1], nums[i2 - 1] = nums[i2 - 1], nums[i1 - 1]
19        
20        p1 = i1
21        p2 = len(nums) - 1
22        while True:
23            if p1 < p2:
24                nums[p1], nums[p2] = nums[p2], nums[p1]
25                p1 += 1
26                p2 -= 1
27            else:
28                break
29                
30        return nums

LeetCode 556. Next Greater Element III (Medium)

We just need to convert an integer to a list of digits. Then the problem is converted to LeetCode 31. Next Permutation (Medium).

 1class Solution:
 2    def nextGreaterElement(self, n: int) -> int:
 3        nums = [int(d) for d in str(n)]
 4        
 5        i1 = len(nums) - 1
 6        while i1 > 0 and nums[i1 - 1] >= nums[i1]:
 7            i1 -= 1
 8            
 9        if i1 == 0:
10            return -1
11        
12        i2 = i1
13        while i2 < len(nums) and nums[i2] > nums[i1 - 1]:
14            i2 += 1
15            
16        nums[i1 - 1], nums[i2 - 1] = nums[i2 - 1], nums[i1 - 1]
17        
18        p1 = i1
19        p2 = len(nums) - 1
20        while True:
21            if p1 < p2:
22                nums[p1], nums[p2] = nums[p2], nums[p1]
23                p1 += 1
24                p2 -= 1
25            else:
26                break
27                
28        nums = int(''.join(str(d) for d in nums))
29        return nums if -2 ** 31 <= nums <= 2 ** 31 - 1 else -1

Two pointers to swap after skipping elements

Below is a template to use two pointers to swap elements of a list s after skipping elements that satisfy some conditions.

 1def templateTwoPointersToSwapAfterSkippingElems(s: str) -> None:
 2    s = list(s)
 3
 4    p1 = 0
 5    p2 = len(s) - 1
 6    while True:
 7      
 8        while p1 <= len(s) - 1 and s[p1] satisfies some conditions:
 9            p1 += 1
10        while p2 >= 0 and s[p2] satisfies some conditions:
11            p2 -= 1
12            
13        if p1 < p2:
14            s[p1], s[p2] = s[p2], s[p1]
15            p1 += 1
16            p2 -= 1
17        else:
18            break

The main difference is that we have two additional while loops in the first while loop to help us skip / ignore some elements (make sure you bear in mind this very commonly used code snippet). Also, never forget the conditions p1 <= len(s) - 1 and p2 >= 0 to avoid the out-of-index error. Some problems that can be solved by this template, as well as their solutions, are as follows.

LeetCode 917. Reverse Only Letters (Easy)

Its solution shown below is a very straightforward application of the template.

 1class Solution:
 2    def reverseOnlyLetters(self, s: str) -> str:
 3        s = list(s)
 4        
 5        p1 = 0
 6        p2 = len(s) - 1
 7        while True:
 8          
 9            while p1 <= len(s) - 1 and not s[p1].isalpha():
10                p1 += 1
11            while p2 >= 0 and not s[p2].isalpha():
12                p2 -= 1
13            
14            if p1 < p2:
15                s[p1], s[p2] = s[p2], s[p1]
16                p1 += 1
17                p2 -= 1
18            else:
19                break
20            
21        return ''.join(s)

LeetCode 905. Sort Array By Parity (Easy)

Its solution shown below is a very straightforward application of the template.

 1class Solution:
 2    def sortArrayByParity(self, s: List[int]) -> List[int]:
 3        p1 = 0
 4        p2 = len(s) - 1
 5        while True:
 6          
 7            while p1 <= len(s) - 1 and s[p1] % 2 == 0:
 8                p1 += 1
 9            while p2 >= 0 and s[p2] % 2 == 1:
10                p2 -= 1
11            
12            if p1 < p2:
13                s[p1], s[p2] = s[p2], s[p1]
14                p1 += 1
15                p2 -= 1
16            else:
17                break
18            
19        return s