Arrays¶
Python implementation ā 12 questions covering core concepts and interview patterns.
ARRAYS ā Core Concepts & Interview Questions
"""
ARRAYS ā Core Concepts & Interview Questions
=============================================
Core Concepts:
āāāāāāāāāāāāāā
⢠Contiguous memory allocation ā O(1) random access by index
⢠Static vs Dynamic arrays (Python lists are dynamic/resizable)
⢠Slicing creates shallow copies: arr[start:stop:step]
⢠Two-pointer technique for sorted/in-place problems
⢠Sliding window for subarray/substring optimizations
⢠Prefix sums for range query optimization
⢠Kadane's Algorithm for maximum subarray sum
Common Patterns:
āāāāāāāāāāāāāāāā
⢠Two Pointers (converging, parallel, slow-fast)
⢠Sliding Window (fixed size, variable size)
⢠Prefix Sum + Hash Map
⢠In-place modification
⢠Cyclic sort (1 to n range)
Key Python Methods:
āāāāāāāāāāāāāāāāāāā
arr.sort(), sorted(arr), arr.append(), arr.pop(), arr[::-1]
bisect_left/right for binary search on sorted arrays
"""
from typing import List, Optional, Tuple
import time
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# QUESTION 1: Two Sum
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def two_sum(nums: List[int], target: int) -> List[int]:
"""
QUESTION:
āāāāāāāāā
Given an array of integers nums and an integer target, return indices
of the two numbers that add up to target. Assume exactly one solution.
You may not use the same element twice.
Example:
Input: nums = [2, 7, 11, 15], target = 9
Output: [0, 1] (2 + 7 = 9)
THOUGHT PROCESS:
āāāāāāāāāāāāāāāā
1. Brute Force: Check every pair ā O(n²), O(1)
2. Better: Use hash map to store {value: index}
- For each number, compute complement = target - num
- If complement exists in map, we found the pair
- Otherwise, add current number to map
- One pass: O(n) time, O(n) space
3. This is optimal since we must examine each element
COMPLEXITY:
āāāāāāāāāā
Time: O(n) ā Single pass through the array
Space: O(n) ā Hash map stores up to n elements
EDGE CASES:
āāāāāāāāāā
⢠Duplicate values ā handled because we return on complement found
⢠Negative numbers ā works with the same logic
⢠No solution ā constraint says exactly one solution exists
"""
seen = {} # value -> index
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return [] # Should never reach here per problem constraints
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# QUESTION 2: Maximum Subarray (Kadane's Algorithm)
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def max_subarray_sum(nums: List[int]) -> int:
"""
QUESTION:
āāāāāāāāā
Find the contiguous subarray (containing at least one number) which
has the largest sum and return its sum.
Example:
Input: nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
Output: 6 (subarray [4, -1, 2, 1] sums to 6)
THOUGHT PROCESS:
āāāāāāāāāāāāāāāā
1. Brute Force: Try all O(n²) subarrays, compute sum ā O(n²)
2. Kadane's Insight:
- At each position, decide: extend current subarray or start fresh?
- If current_sum + nums[i] < nums[i], it's better to start fresh
- Track global maximum across all positions
3. Why it works: the best subarray ending at i is either nums[i] alone
or nums[i] appended to the best subarray ending at i-1 (optimal
substructure). It is really a 1-D DP with the table collapsed to
one variable.
COMPLEXITY:
āāāāāāāāāā
Time: O(n) ā Single pass with O(1) operations per element
Space: O(1) ā Only two variables needed
EDGE CASES:
āāāāāāāāāā
⢠All negative ā answer is the largest single element (hence we seed
with nums[0], not 0; seeding with 0 is a classic bug)
⢠Empty input ā undefined by the problem; raise or return a sentinel
⢠Java/Go: running sums can overflow a 32-bit int; use long / int64
FOLLOW-UPS INTERVIEWERS ASK:
āāāāāāāāāāāāāāāāāāāāāāāāāāāā
⢠Return the indices ā track where the current run started
⢠Circular array ā max(normal Kadane, total - min-subarray), unless all
elements are negative
"""
max_ending_here = nums[0]
max_so_far = nums[0]
for i in range(1, len(nums)):
# Start new or extend? ā key insight of Kadane's
max_ending_here = max(nums[i], max_ending_here + nums[i])
max_so_far = max(max_so_far, max_ending_here)
return max_so_far
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# QUESTION 3: Product of Array Except Self
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def product_except_self(nums: List[int]) -> List[int]:
"""
QUESTION:
āāāāāāāāā
Given an integer array nums, return an array output such that
output[i] is the product of all elements of nums except nums[i].
Must run in O(n) without using division.
Example:
Input: nums = [1, 2, 3, 4]
Output: [24, 12, 8, 6]
THOUGHT PROCESS:
āāāāāāāāāāāāāāāā
1. With division: total_product / nums[i] ā but division not allowed
2. Key insight: output[i] = product of elements to the left * product to the right
3. Two-pass approach:
- Left pass: Calculate running product from left side
- Right pass: Multiply with running product from right side
4. Optimization: Use output array itself for left products, then
multiply with right products using a single variable
COMPLEXITY:
āāāāāāāāāā
Time: O(n) ā Two passes over the array
Space: O(1) ā Output array doesn't count as extra space
EDGE CASES:
āāāāāāāāāā
⢠One zero ā every output is 0 except at the zero's index
⢠Two or more zeros ā every output is 0
(both fall out of prefix/suffix products naturally; this is also why
the "total / nums[i]" trick is wrong even when division is allowed)
"""
n = len(nums)
output = [1] * n
# Left pass: output[i] = product of elements to the left of i
left_product = 1
for i in range(n):
output[i] = left_product
left_product *= nums[i]
# Right pass: multiply with product of elements to the right
right_product = 1
for i in range(n - 1, -1, -1):
output[i] *= right_product
right_product *= nums[i]
return output
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# QUESTION 4: Container With Most Water
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def max_area(height: List[int]) -> int:
"""
QUESTION:
āāāāāāāāā
Given an array height where height[i] represents vertical lines,
find two lines that together with the x-axis form a container that
holds the most water.
Example:
Input: height = [1, 8, 6, 2, 5, 4, 8, 3, 7]
Output: 49 (between indices 1 and 8, area = 7 * 7 = 49)
THOUGHT PROCESS:
āāāāāāāāāāāāāāāā
1. Brute Force: Check all O(n²) pairs ā too slow
2. Two-pointer insight:
- Start with widest container (pointers at ends)
- Area = min(height[left], height[right]) * (right - left)
- The limiting factor is the shorter line
- To find a larger area, we must move the shorter line inward
(moving the taller line can only decrease or equal the area)
3. Why moving the shorter line works:
- If we move the taller line, area can only decrease
(width decreases, height ⤠min(shorter, taller) = shorter)
- Moving the shorter line may find a taller line
COMPLEXITY:
āāāāāāāāāā
Time: O(n) ā Single pass with two pointers
Space: O(1) ā Only variables
"""
left, right = 0, len(height) - 1
max_area_val = 0
while left < right:
# Calculate current area
h = min(height[left], height[right])
w = right - left
max_area_val = max(max_area_val, h * w)
# Move the shorter line inward
if height[left] < height[right]:
left += 1
else:
right -= 1
return max_area_val
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# QUESTION 5: Find All Duplicates in Array
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def find_duplicates(nums: List[int]) -> List[int]:
"""
QUESTION:
āāāāāāāāā
Given an array of integers, 1 ⤠a[i] ⤠n (n = size of array),
some elements appear twice and others once. Find all elements that
appear twice. Must run in O(n) time and use O(1) extra space.
Example:
Input: [4, 3, 2, 7, 8, 2, 3, 1]
Output: [2, 3]
THOUGHT PROCESS:
āāāāāāāāāāāāāāāā
1. Count frequencies with hash map ā O(n) time, O(n) space; violates space
2. Key insight: Values are in range [1, n], which match array indices
- Negate the element at index (value - 1) as a marker
- If already negative, we've seen it before ā it's a duplicate
3. Why negation works:
- Each value maps to a unique index
- Negation marks "visited" without losing information
- Restore by taking abs() for index calculation
COMPLEXITY:
āāāāāāāāāā
Time: O(n) ā Single pass
Space: O(1) ā Excluding output list
EDGE CASES:
āāāāāāāāāā
⢠The trick mutates the input; say so in the interview and restore it
(the second loop below) if the caller still needs the array
⢠Only valid because values are in [1, n]; otherwise use a hash set
"""
result = []
for num in nums:
idx = abs(num) - 1 # Value maps to index
if nums[idx] < 0:
result.append(abs(num)) # Already marked ā duplicate
else:
nums[idx] = -nums[idx] # Mark as visited
# Restore array (optional, good practice)
for i in range(len(nums)):
nums[i] = abs(nums[i])
return result
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# QUESTION 6: Three Sum (Triplets that sum to zero)
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def three_sum(nums: List[int]) -> List[List[int]]:
"""
QUESTION:
āāāāāāāāā
Given an integer array, return all unique triplets [nums[i], nums[j],
nums[k]] such that i != j != k and nums[i] + nums[j] + nums[k] = 0.
Example:
Input: nums = [-1, 0, 1, 2, -1, -4]
Output: [[-1, -1, 2], [-1, 0, 1]]
THOUGHT PROCESS:
āāāāāāāāāāāāāāāā
1. Brute Force: Check all O(n³) triplets ā too slow
2. Sort + Two Pointers:
- Sort the array (enables two-pointer + deduplication)
- Fix one element, then use two pointers for the remaining two
- For each nums[i], find pairs in the subarray [i+1:] that sum to -nums[i]
3. Deduplication strategy:
- Skip duplicate values for i (if nums[i] == nums[i-1])
- After finding a valid pair, skip duplicates at both ends
4. Pruning: If nums[i] > 0, break (can't sum to 0 with sorted array)
COMPLEXITY:
āāāāāāāāāā
Time: O(n²) ā Sorting O(n log n) + nested loops O(n²)
Space: O(1) extra besides the output, but sorting needs space too
(Python's Timsort is O(n) worst case; sorts the input in place)
EDGE CASES:
āāāāāāāāāā
⢠Fewer than 3 elements ā loop body never runs, returns []
⢠All zeros [0,0,0,0] ā exactly one triplet thanks to the dedup skips
⢠The input list is sorted in place; copy first if the caller cares
"""
nums.sort()
result = []
n = len(nums)
for i in range(n - 2):
# Skip duplicates for the fixed element
if i > 0 and nums[i] == nums[i - 1]:
continue
# Pruning: smallest element > 0, no triplets possible
if nums[i] > 0:
break
left, right = i + 1, n - 1
target = -nums[i]
while left < right:
current_sum = nums[left] + nums[right]
if current_sum == target:
result.append([nums[i], nums[left], nums[right]])
# Skip duplicates at both ends
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
elif current_sum < target:
left += 1
else:
right -= 1
return result
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# QUESTION 7: Merge Intervals
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def merge_intervals(intervals: List[List[int]]) -> List[List[int]]:
"""
QUESTION:
āāāāāāāāā
Given an array of intervals where intervals[i] = [start, end],
merge all overlapping intervals and return the result.
Example:
Input: [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
THOUGHT PROCESS:
āāāāāāāāāāāāāāāā
1. Sort by start time to ensure we process in order
2. Compare current interval's end with next interval's start:
- If current_end >= next_start ā they overlap, merge (extend end)
- Otherwise ā no overlap, add current to result, move to next
3. Key insight: Only need to check start ⤠current_end for overlap
Since sorted by start, if next_start > current_end, it's disjoint
COMPLEXITY:
āāāāāāāāāā
Time: O(n log n) ā Sorting is the bottleneck
Space: O(n) ā The output (plus the sort's own buffer)
EDGE CASES:
āāāāāāāāāā
⢠Touching intervals [1,4],[4,5] ā merged (we use <=); ask whether
the interviewer treats closed or half-open intervals
⢠One interval contains another [1,10],[2,3] ā the max() keeps 10
⢠Don't alias the input: appending intervals[0] itself and then
mutating merged[-1] would silently rewrite the caller's data
"""
if not intervals:
return []
# Sort by start time (sorted() leaves the caller's list untouched)
intervals = sorted(intervals, key=lambda x: x[0])
merged = [list(intervals[0])]
for start, end in intervals[1:]:
last_end = merged[-1][1]
if start <= last_end:
# Overlap detected ā merge by extending the end
merged[-1][1] = max(last_end, end)
else:
# No overlap ā add as new interval
merged.append([start, end])
return merged
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# QUESTION 8: First Missing Positive
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def first_missing_positive(nums: List[int]) -> int:
"""
QUESTION:
āāāāāāāāā
Given an unsorted integer array, find the smallest positive integer
that is not present. Must run in O(n) time and O(1) space.
Example:
Input: [3, 4, -1, 1]
Output: 2
THOUGHT PROCESS:
āāāāāāāāāāāāāāāā
1. The answer is in range [1, n+1] where n = len(nums)
- If all numbers 1..n are present, answer is n+1
2. Use array itself as a hash set via cyclic placement:
- Place each number x at index (x-1) if x is in [1, n]
- After placement, scan for first index where value != index+1
3. Cyclic swap technique:
- While nums[i] is in [1, n] and not in its correct position:
swap nums[i] with nums[nums[i] - 1]
- This is O(n) because each swap puts one number in its place
COMPLEXITY:
āāāāāāāāāā
Time: O(n) ā Every swap puts one value into its final slot, so there
are at most n swaps in total across all iterations of the while
Space: O(1) ā In-place swapping (mutates the input)
EDGE CASES:
āāāāāāāāāā
⢠Duplicates [1,1] ā the loop condition compares against the value
already at the target slot (not against i+1), so it cannot spin forever
⢠All negatives / zeros ā answer is 1
"""
n = len(nums)
# Place each number in its correct position using cyclic sort
for i in range(n):
while 1 <= nums[i] <= n and nums[i] != nums[nums[i] - 1]:
# Swap to put nums[i] in its correct position
correct_idx = nums[i] - 1
nums[i], nums[correct_idx] = nums[correct_idx], nums[i]
# Find the first index where value != index + 1
for i in range(n):
if nums[i] != i + 1:
return i + 1
return n + 1 # All numbers 1..n present
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# QUESTION 9: Rotate Array
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def rotate_array(nums: List[int], k: int) -> None:
"""
QUESTION:
āāāāāāāāā
Given an array, rotate the array to the right by k steps, in-place.
Example:
Input: nums = [1,2,3,4,5,6,7], k = 3
Output: [5,6,7,1,2,3,4]
THOUGHT PROCESS:
āāāāāāāāāāāāāāāā
1. Naive: Pop and insert at front k times ā O(nĀ·k), O(1)
2. Extra array: Copy to new array at (i+k)%n positions ā O(n), O(n)
3. Optimal: Reverse-based approach (tricky but brilliant):
- Reverse entire array: [7,6,5,4,3,2,1]
- Reverse first k elements: [5,6,7,4,3,2,1]
- Reverse remaining n-k elements: [5,6,7,1,2,3,4]
4. Why reversal works: Visualize as taking last k elements to front
COMPLEXITY:
āāāāāāāāāā
Time: O(n) ā Three reverses, each O(n)
Space: O(1) ā In-place modifications
EDGE CASES:
āāāāāāāāāā
⢠k > n ā reduce with k %= n first
⢠Empty array ā guard before the modulo (k % 0 raises)
"""
n = len(nums)
if n == 0:
return
k %= n # Handle k > n
def reverse(start: int, end: int) -> None:
while start < end:
nums[start], nums[end] = nums[end], nums[start]
start += 1
end -= 1
reverse(0, n - 1) # Reverse entire array
reverse(0, k - 1) # Reverse first k elements
reverse(k, n - 1) # Reverse remaining n-k elements
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# QUESTION 10: Longest Consecutive Sequence
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def longest_consecutive(nums: List[int]) -> int:
"""
QUESTION:
āāāāāāāāā
Given an unsorted array of integers, find the length of the longest
consecutive elements sequence. Must run in O(n) time.
Example:
Input: [100, 4, 200, 1, 3, 2]
Output: 4 (sequence [1, 2, 3, 4])
THOUGHT PROCESS:
āāāāāāāāāāāāāāāā
1. Sort and scan ā O(n log n), too slow
2. Hash Set approach:
- Insert all numbers into a set (O(1) membership check)
- For each number, check if it's the START of a sequence
(i.e., num-1 is NOT in the set)
- If it is, count consecutive numbers in the set
3. Why O(n) overall:
- Each number is visited at most twice (once in outer loop,
once as part of a sequence count)
- Only sequence starters trigger inner while loops
COMPLEXITY:
āāāāāāāāāā
Time: O(n) ā Each element processed constant times
Space: O(n) ā Hash set storage
"""
num_set = set(nums)
max_length = 0
for num in num_set:
# Only process if this is the START of a sequence
if num - 1 not in num_set:
current = num
length = 1
while current + 1 in num_set:
current += 1
length += 1
max_length = max(max_length, length)
return max_length
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# QUESTION 11: Best Time to Buy and Sell Stock
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def max_profit(prices: List[int]) -> int:
"""
QUESTION:
āāāāāāāāā
prices[i] is a stock's price on day i. Choose one day to buy and a
later day to sell. Return the maximum profit, or 0 if no profit is
possible.
Example:
Input: [7, 1, 5, 3, 6, 4]
Output: 5 (buy at 1, sell at 6)
THOUGHT PROCESS:
āāāāāāāāāāāāāāāā
1. Brute Force: Try every (buy, sell) pair with buy < sell ā O(n²)
2. Key insight: if we sell on day i, the best buy day is the cheapest
price seen BEFORE i. So scan once, keeping the running minimum.
3. Profit on day i = prices[i] - min_so_far; track the max of that.
4. Same shape as Kadane's: it is max subarray on the day-to-day deltas.
COMPLEXITY:
āāāāāāāāāā
Time: O(n) ā Single pass
Space: O(1) ā Two variables
EDGE CASES:
āāāāāāāāāā
⢠Strictly decreasing prices ā answer 0 (never trade)
⢠Empty or single-day input ā 0
FOLLOW-UPS INTERVIEWERS ASK:
āāāāāāāāāāāāāāāāāāāāāāāāāāāā
⢠Unlimited transactions (II) ā sum every positive day-to-day delta
⢠At most k transactions / cooldown / fee ā state-machine DP
"""
min_price = float("inf")
best = 0
for price in prices:
min_price = min(min_price, price) # Cheapest buy so far
best = max(best, price - min_price) # Sell today?
return best
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# QUESTION 12: Trapping Rain Water
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def trap_rain_water(height: List[int]) -> int:
"""
QUESTION:
āāāāāāāāā
Given n non-negative bar heights (width 1), compute how much water is
trapped after raining.
Example:
Input: [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
Output: 6
THOUGHT PROCESS:
āāāāāāāāāāāāāāāā
1. Water above bar i = min(max_left[i], max_right[i]) - height[i].
Brute force recomputes both maxima for each i ā O(n²).
2. Precompute prefix-max and suffix-max arrays ā O(n) time, O(n) space.
3. Two pointers drop the arrays to O(1) space:
- Keep left_max and right_max as we walk inward.
- If left_max < right_max, the water at `left` is bounded by
left_max: there is a wall at least right_max tall somewhere on
the right, so the right side can't be the limiting one.
Add left_max - height[left] and advance left.
- Otherwise, symmetric on the right.
4. Alternative: monotonic decreasing stack that fills water layer by
layer when a taller bar arrives (also O(n)).
COMPLEXITY:
āāāāāāāāāā
Time: O(n) ā Each index visited once
Space: O(1) ā Four variables
EDGE CASES:
āāāāāāāāāā
⢠Fewer than 3 bars ā 0
⢠Monotonic heights ā 0 (no left or right wall)
⢠Plateaus and equal walls ā handled by the `<` / else split
"""
left, right = 0, len(height) - 1
left_max = right_max = 0
water = 0
while left < right:
left_max = max(left_max, height[left])
right_max = max(right_max, height[right])
if left_max < right_max:
water += left_max - height[left] # Left wall is the bottleneck
left += 1
else:
water += right_max - height[right] # Right wall is the bottleneck
right -= 1
return water
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
# DEMO: Run all array problems
# āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
def demo():
print("=" * 70)
print("ARRAYS ā Interview Questions Demo")
print("=" * 70)
# Q1: Two Sum
print("\n1ļøā£ Two Sum")
print("-" * 40)
nums, target = [2, 7, 11, 15], 9
result = two_sum(nums, target)
print(f" Input: nums={nums}, target={target}")
print(f" Output: {result} (nums[{result[0]}] + nums[{result[1]}] = {nums[result[0]] + nums[result[1]]})")
# Q2: Maximum Subarray
print("\n2ļøā£ Maximum Subarray (Kadane's Algorithm)")
print("-" * 40)
nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
result = max_subarray_sum(nums)
print(f" Input: {nums}")
print(f" Maximum Subarray Sum: {result}")
# Q3: Product Except Self
print("\n3ļøā£ Product of Array Except Self")
print("-" * 40)
nums = [1, 2, 3, 4]
result = product_except_self(nums)
print(f" Input: {nums}")
print(f" Output: {result}")
# Q4: Container With Most Water
print("\n4ļøā£ Container With Most Water")
print("-" * 40)
height = [1, 8, 6, 2, 5, 4, 8, 3, 7]
result = max_area(height)
print(f" Input: {height}")
print(f" Max Area: {result}")
# Q5: Find Duplicates
print("\n5ļøā£ Find All Duplicates in Array")
print("-" * 40)
nums = [4, 3, 2, 7, 8, 2, 3, 1]
result = find_duplicates(nums[:]) # Pass copy
print(f" Input: [4, 3, 2, 7, 8, 2, 3, 1]")
print(f" Duplicates: {result}")
# Q6: Three Sum
print("\n6ļøā£ Three Sum")
print("-" * 40)
nums = [-1, 0, 1, 2, -1, -4]
result = three_sum(nums[:]) # Pass copy (three_sum sorts in place)
print(f" Input: {nums}")
print(f" Triplets: {result}")
# Q7: Merge Intervals
print("\n7ļøā£ Merge Intervals")
print("-" * 40)
intervals = [[1, 3], [2, 6], [8, 10], [15, 18]]
result = merge_intervals(intervals)
print(f" Input: {intervals}")
print(f" Merged: {result}")
# Q8: First Missing Positive
print("\n8ļøā£ First Missing Positive")
print("-" * 40)
nums = [3, 4, -1, 1]
result = first_missing_positive(nums[:]) # Pass copy
print(f" Input: [3, 4, -1, 1]")
print(f" First Missing Positive: {result}")
# Q9: Rotate Array
print("\n9ļøā£ Rotate Array")
print("-" * 40)
nums = [1, 2, 3, 4, 5, 6, 7]
k = 3
original = nums[:]
rotate_array(nums, k)
print(f" Input: {original}, k={k}")
print(f" Rotated: {nums}")
# Q10: Longest Consecutive Sequence
print("\nš Longest Consecutive Sequence")
print("-" * 40)
nums = [100, 4, 200, 1, 3, 2]
result = longest_consecutive(nums)
print(f" Input: {nums}")
print(f" Longest Consecutive Length: {result}")
# Q11: Best Time to Buy and Sell Stock
print("\n1ļøā£1ļøā£ Best Time to Buy and Sell Stock")
print("-" * 40)
prices = [7, 1, 5, 3, 6, 4]
print(f" Input: {prices}")
print(f" Max Profit: {max_profit(prices)}")
# Q12: Trapping Rain Water
print("\n1ļøā£2ļøā£ Trapping Rain Water")
print("-" * 40)
height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
print(f" Input: {height}")
print(f" Trapped Water: {trap_rain_water(height)}")
print("\n" + "=" * 70)
if __name__ == "__main__":
demo()