Two Pointers - kjingers/Grokking-Notes GitHub Wiki
Dealing with SORTED arrays or Linked Lists and need to find set of elements that fulfill certain constraints.
Pair with Target Sum
def pair_with_targetsum(arr, target_sum):
leftPointer = 0
rightPointer = len(arr) - 1
while leftPointer < rightPointer:
currSum = arr[leftPointer] + arr[rightPointer]
if currSum is target_sum:
return [leftPointer, rightPointer]
if currSum < target_sum:
leftPointer += 1
elif currSum > target_sum:
rightPointer -= 1
return [-1, -1]
Remove Duplicates from Sorted Array in Place
Use a left pointer to point to the position for the non duplicate number. The right pointer just loops from 1 to len(arr)
def remove_duplicates(arr):
nextNum = 1
for i in range(1, len(arr)):
if arr[nextNum - 1] is not arr[i]:
arr[nextNum] = arr[i]
nextNum += 1
return nextNum
Squaring a Sorted Array
Since the largest square can be on the left (negative) or right, we use two pointers starting from each end. While left < right, we compare the squares and put the larger one in the largest index in the output array.
def make_squares(arr):
squares = [0 for _ in range(len(arr))]
left, right = 0, len(arr) - 1
squareIdx = len(arr) - 1
while left < right:
leftSquare = arr[left] * arr[left]
rightSquare = arr[right] * arr[right]
if leftSquare > rightSquare:
squares[squareIdx] = leftSquare
left += 1
else:
squares[squareIdx] = rightSquare
right -= 1
squareIdx -= 1
return squares
Triplet Sum to Zero
First must sort the array. The outer loop loops from index 0 to len(arr). At each index, we make -1 * arr[i]
the target, and essentially do two sum on the rest of the array to the right of i. They key here is to avoid dumplicates by skipping i if arr[i] == arr[i-1]
, or l if a[l] == arr[l-1]
after we found a match and increment l.
def search_triplets(arr):
triplets = []
arr.sort()
n = len(arr)
for i in range(n):
# This number is same as previous. Skip
if i > 0 and arr[i] == arr[i-1]:
continue
target = -1 * arr[i]
l = i + 1
r = n - 1
while l < r:
if arr[l] + arr[r] == target:
triplets.append([arr[i], arr[l], arr[r]])
l += 1
while l < r and arr[l] == arr[l-1]:
l += 1
elif arr[l] + arr[r] < target:
l += 1
else:
r -= 1
return triplets
Triplet Sum Close to Target
Similar to above, where outer loop from index 0 to len(array) - 1
. The only idfference is we keep track of the current smallest diff and the current sum corresponding to the smallest diff. That way, if we get a triplet with the same smallest diff, we check to see if the sum is smaller.
import math
def triplet_sum_close_to_target(arr, target_sum):
n = len(arr)
runningSum = math.inf
runningDiff = math.inf
arr.sort()
for i in range(n):
if i > 0 and arr[i] is arr[i-1]:
continue
l = i + 1
r = n - 1
while l < r:
curSum = arr[i] + arr[l] + arr[r]
diff = abs(target_sum - curSum)
if diff is runningDiff and curSum < runningSum:
runningSum = curSum
elif diff < runningDiff:
runningSum = curSum
runningDiff = diff
if curSum <= target_sum:
l += 1
else:
r -= 1
return runningSum
Triplet with Smaller Sum
Similar to above. This time, once we get a sum less than target, we add r - l
to target, since right can point to any number between itself and left to still get a sum smaller than target
def triplet_with_smaller_sum(arr, target):
count = 0
n = len(arr)
arr.sort()
for i in range(n):
if i > 0 and arr[i] is arr[i-1]:
continue
l = i + 1
r = n - 1
while l < r:
curSum = arr[i] + arr[l] + arr[r]
if curSum < target:
count += r - l
l += 1
while arr[l] is arr[l - 1]:
l += 1
else:
r -= 1
return count
Subarrays with Product Less than Target
This is actually a sliding window problem. The trick here is to avoid duplicate subarrays, we use a deque.appendleft to only add subarrays where arr[right] is the right most value.
from collections import deque
def find_subarrays(arr, target):
result = []
left = 0
product = 1
for right in range(len(arr)):
product *= arr[right]
while product >= target and left < right:
product /= arr[left]
left += 1
# To avoid duplication, we will go through all subarrays where arr[right] is the
# right-most value
queue = deque()
for i in range(right, left - 1, -1):
queue.appendleft(arr[i])
result.append(list(queue))
return result
Dutch National Flag Problem
Have a low
pointer that points to the place for the next 0, and a high
pointer that points to the place for the next 2. Then loop i
while i <= high
, moving 0s to low pointer and moving 2s to the high pointer - and incrementing / decrementing accordingly.
def dutch_flag_sort(arr):
n = len(arr)
low, high = 0, n - 1
i = 0
while i <= high:
if arr[i] is 0:
arr[low], arr[i] = arr[i], arr[low]
i += 1
low += 1
elif arr[i] is 2:
arr[high], arr[i] = arr[i], arr[high]
high -= 1
else:
i += 1
return arr
Quadruple Sum to Target
Similar to Triplet Sum to Target. Only addition is we have another outer loop j
before running twoSum
on the remaining array.
def twoSum(arr, target, first, second, quadruplets):
left = second + 1
right = len(arr) - 1
while left < right:
curSum = arr[first] + arr[second] + arr[left] + arr[right]
if target == curSum:
quadruplets.append([arr[first], arr[second], arr[left], arr[right]])
left += 1
while left < right and arr[left] == arr[left - 1]:
left += 1
elif curSum < target:
left += 1
else:
right -= 1
def search_quadruplets(arr, target):
quadruplets = []
arr.sort()
for i in range(len(arr) - 3):
if i > 0 and arr[i] == arr[i-1]:
continue
for j in range(i + 1, len(arr) - 2):
if j > i + 1 and arr[j] == arr[j - 1]:
continue
twoSum(arr, target, i, j, quadruplets)
return quadruplets
Backspace Compare
Definitely need to do this one again.
def getNextChar(str, index):
backspaceCount = 0
while index >= 0:
if str[index] is '#':
backspaceCount += 1
elif backspaceCount > 0:
backspaceCount -= 1
else:
break
index -= 1
return index
def backspace_compare(str1, str2):
i1 = len(str1) - 1
i2 = len(str2) - 1
while i1 >= 0 or i2 >= 0:
next1 = getNextChar(str1, i1)
next2 = getNextChar(str2, i2)
# Both strings finished
if next1 < 0 and next2 < 0:
return True
# Only of the Strings finished
elif next1 < 0 or next1 < 0:
return False
elif str1[next1] != str2[next2]:
return False
i1 = next1 - 1
i2 = next2 - 1
return True
Minimum Window Sort
import math
def shortest_window_sort(arr):
n = len(arr)
if n <= 1:
return 0
left = 0
right = n - 1
while left < n - 1 and arr[left+1] >= arr[left]:
left += 1
if left == n - 1:
return 0
while right > 0 and arr[right-1] <= arr[right]:
right -= 1
subarrayMax = -math.inf
subarrayMin = math.inf
for i in range(left, right + 1):
subarrayMax = max(subarrayMax, arr[i])
subarrayMin = min(subarrayMin, arr[i])
while left > 0 and arr[left-1] > subarrayMin:
left -= 1
while right < n-1 and arr[right + 1] < subarrayMax:
right += 1
return right - left + 1