1. Array
Brief
In most interviews, Data Structure and Algorithms revolves around array only with various algorithms inside it like two pointer, sliding window, slow and fast pointer etc.
Find a Pair That Sums to a Target
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
# When sorting is not allowed
hashMap = {}
for i in range(len(nums)):
complement = target - nums[i]
if complement in hashMap:
return [hashMap[complement], i]
else:
hashMap[nums[i]] = i
return [-1, -1]
def twoSum_sorted(self, nums: List[int], target: int) -> List[int]:
# When array is sorted
left_pointer = 0
right_pointer = len(numbers) - 1
while left_pointer < right_pointer:
current_sum = numbers[left_pointer] + numbers[right_pointer]
if current_sum == target_sum:
return True
if current_sum < target_sum:
left_pointer += 1
else:
right_pointer -= 1
return False
Best Time to Buy and Sell Stocks
class Solution:
def maxProfit(self, prices: List[int]) -> int:
# Brute Force but TLE exceeded
mx = 0
for i in range(len(prices)):
for j in range(i+1,len(prices)):
if prices[i] < prices[j]:
mx = max(mx,prices[j] - prices[i])
return mx
def maxProfit_Optimized(self, prices: List[int]) -> int:
# Kadane's Algo
# Track the minimum at each position and calculate the maximum profit by subtracting current price with min_price. min_price = inf
maxProfit = 0
for i in range(len(prices)):
min_price = min(min_price, prices[i])
maxProfit = max(maxProfit, prices[i] - min_price)
return maxProfit
Contains Duplicate
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
hashSet = set()
for num in nums:
if num in hashSet:
return True
else:
hashSet.add(num)
return False
Product of Array Except Self
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
# Brute Force
n = len(nums)
result = [1]*n
for i in range(n):
product = 1
for j in range(n):
if i == j:
continue
elif nums[j] != 0:
product *= nums[j]
else:
product = 0
break
result[i] = product
return result
def productExceptSelf_Optimized(self, nums: List[int]) -> List[int]:
n = len(nums)
prefix = [1] * n
postfix = [1] * n
res = [1] * n
for i in range(1, n):
# calculate prefix of i = (prefix of i - 1) * (nums[i - 1])
prefix[i] = prefix[i - 1] * nums[i - 1]
# Postfix
# postfix starts at 1 = the postfix of len(n) - 1
# start reverse iteration at: len(n) - 2
# tc: iterate list O(n)
for i in range(n-2, -1, -1):
# calculate postfix of i = (postfix of i + 1) * (nums[i + 1])
postfix[i] = postfix[i + 1] * nums[i + 1]
# Result
# tc: iterate list O(n)
for i in range(n):
# calculate product except self for i = (prefix of i) * (postfix of i)
res[i] = prefix[i] * postfix[i]
return res
def productExceptSelf_Optimized2(self, nums: List[int]) -> List[int]:
n = len(nums)
res = [1] * n
prefix = 1
for i in range(n):
res[i] *= prefix
prefix *= nums[i]
postfix = 1
for i in range(n-1, -1, -1):
res[i] *= postfix
postfix *= nums[i]
return res
Maximum Sum Subarray
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
# Brute force is to loop through each subarray and calculate sum and then it's max.
# max_sum = -inf
# if len(nums) == 1:
# return nums[0]
# for i in range(len(nums)):
# curr_sum = 0
# for j in range(i, len(nums)):
# curr_sum += nums[j]
# max_sum = max(max_sum, curr_sum)
# return max_sum
# Below is the optimized approach, Calculate the sum, if it goes negative, it means, we are getting some negative number which is not helping us, so update the cur_sum to 0 again and look for next elements.
max_sum = -inf
cur_sum = 0
for i in range(len(nums)):
if cur_sum < 0:
cur_sum = 0
cur_sum += nums[i]
max_sum = max(max_sum, cur_sum)
return max_sum
Container with Most Water
def maxArea(self, height: List[int]) -> int:
left = 0
right = len(height) - 1
maxArea = 0
while left < right :
area = (right - left) * min(height[left], height[right])
maxArea = max(maxArea, area)
if height[left] < height[right]:
left += 1
else:
right -= 1
return maxArea
Rotate Array
def rotate(self, nums: List[int], k: int) -> None:
n = len(nums)
k = k % n
rotated = [0] * n
for i in range(n):
rotated[(i + k) % n] = nums[i]
for i in range(n):
nums[i] = rotated[i]
# For example, If k = 9, rotated position should be
# (current position + remainder) % length of input array
# [1,2,3,4,5,6,7] -> Input
# ↓
# [6,7,1,2,3,4,5] -> Output
# For 1(= index 0), (0 + 2) % 7 = 2
# For 2(= index 1), (1 + 2) % 7 = 3
# For 6(= index 5), (5 + 2) % 7 = 0
# For 7(= index 6), (6 + 2) % 7 = 1
Search in a Rotated Sorted Array
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
if nums[left] <= nums[mid]:
if nums[left] <= target < nums[mid]:
# Left Sorted Array
right = mid - 1
else:
left = mid + 1 # move to right sorted array, if didn't find target at left
else:
if nums[mid] < target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return -1
Minimum in a Rotated Sorted Array
class Solution:
def findMin(self, nums: List[int]) -> int:
# In rotated sorted array, we have that pattern like, we'll be having maximum element on left sorted array last element and minimum is at right sorted array first element.
res = nums[0]
l, r = 0, len(nums) - 1
while l <= r:
if nums[l] < nums[r]:
res = min(nums[l], res)
m = (l + r) // 2
res = min(res, nums[m])
if nums[m] >= nums[l]: # we are in left sorted array, move to right, there we have minimum
l = m + 1
else:
r = m - 1
return res