Arrays: Medium Level Questions & Solutions
This section covers medium-difficulty array problems commonly encountered in technical interviews and real-world software engineering. Each problem includes a detailed explanation of the core concept, step-by-step example walkthroughs, Python source code, and time/space complexity analysis.
1. Majority Element (Boyer-Moore Voting Algorithm)
Problem Description
Given an array nums of size N, find the majority element. The majority element is defined as the element that appears strictly more than floor(N / 2) times in the array.
If no majority element exists, return -1.
Intuition & Approach
1. Naive / Hash Map Approach
A straightforward way to solve this is using a hash map (or dictionary) to count the frequency of each element:
- Traverse the array and store element frequencies in a hash map.
- Iterate through the hash map to find an element with frequency >= floor(N / 2).
- Complexity: Time O(N), Space O(N).
2. Boyer-Moore Voting Algorithm (O(1) Extra Space)
The Boyer-Moore Voting Algorithm allows us to find the majority candidate in linear time O(N) using only O(1) auxiliary space.
Key Insight: If we pair up different elements in the array and cancel them out, the majority element (which appears more than half the time) will always remain at the end.
Algorithm Steps:
- Candidate Finding Phase:
- Maintain a
candidatevariable and acountcounter initialized to0. - Iterate through
nums:- If
count == 0, assigncandidate = current_elementand resetcount = 1. - Else if
current_element == candidate, incrementcount += 1. - Else, decrement
count -= 1.
- If
- Maintain a
- Verification Phase:
- Because a majority element is not guaranteed in every input array, verify the
candidateby counting its actual occurrences innums. - If
count >= len(nums) // 2, returncandidate. Otherwise, return-1.
- Because a majority element is not guaranteed in every input array, verify the
Example Walkthrough
Input: nums = [7, 0, 0, 1, 7, 7, 2, 7, 7]
(N = 9, threshold floor(9/2) = 4)
Phase 1: Candidate Selection
| Step | Element | candidate | count | Action |
|---|---|---|---|---|
| 0 | 7 | 7 | 1 | count was 0 → set candidate = 7, count = 1 |
| 1 | 0 | 7 | 0 | 0 != 7 → count decremented to 0 |
| 2 | 0 | 0 | 1 | count was 0 → set candidate = 0, count = 1 |
| 3 | 1 | 0 | 0 | 1 != 0 → count decremented to 0 |
| 4 | 7 | 7 | 1 | count was 0 → set candidate = 7, count = 1 |
| 5 | 7 | 7 | 2 | 7 == 7 → count incremented to 2 |
| 6 | 2 | 7 | 1 | 2 != 7 → count decremented to 1 |
| 7 | 7 | 7 | 2 | 7 == 7 → count incremented to 2 |
| 8 | 7 | 7 | 3 | 7 == 7 → count incremented to 3 |
Result Candidate: 7
Phase 2: Candidate Verification
- Count occurrences of
7innums:5times. - Check condition:
5 >= 9 // 2(5 >= 4) isTrue. - Output:
7
Python Solution
class Solution:
def majority_elements(self, nums: list) -> int:
candidate = -1
count = 0
for index, el in enumerate(nums):
if count == 0:
candidate = el
count = 1
elif candidate == el:
count += 1
else:
count -= 1
count = 0
for el in nums:
if el == candidate: count += 1
if count >= len(nums) // 2:
return candidate
return -1
# main function for the program
def main():
sol = Solution()
nums = [7, 0, 0, 1, 7, 7, 2, 7, 7]
print(sol.majority_elements(nums))
# driver code for the program
if __name__ == '__main__':
main()
Complexity Analysis
-
Time Complexity: O(N)
- Phase 1 takes O(N) time for candidate selection.
- Phase 2 takes O(N) time for verification.
- Total time complexity is 2 × O(N) = O(N).
-
Space Complexity: O(1)
- Operates in-place using constant auxiliary variables (
candidateandcount).
- Operates in-place using constant auxiliary variables (
2. Leaders in an Array
Problem Description
Given an array nums, find all the leader elements in the array. An element is considered a leader if it is strictly greater than all elements present to its right.
Note: The rightmost element is always considered a leader because there are no elements to its right.
Intuition & Approach
1. Naive Nested Loop (O(N²))
For each element at index i, loop through all elements from i + 1 to N - 1 to check if nums[i] is greater than every element to its right.
2. Optimal Right-to-Left Scan (O(N))
Instead of scanning left-to-right, scan the array from right to left:
- Keep track of the maximum element seen so far from the right end, stored in
right_most. - The last element
nums[-1]is always a leader. Initializeright_most = nums[-1]and appendnums[-1]toans. - Loop backwards from
len(nums) - 2down to0:- If
nums[index] > right_most: We found a new leader! Updateright_most = nums[index]and append it toans.
- If
- Reverse
ans(ans[::-1]) to return the leaders in their original left-to-right order.
Example Walkthrough
Input: nums = [1, 2, 5, 3, 1, 2]
| Step | Index i | Value nums[i] | right_most | Comparison | Action | ans state |
|---|---|---|---|---|---|---|
| Initial | 5 | 2 | 2 | — | nums[-1] is leader | [2] |
| 1 | 4 | 1 | 2 | 1 > 2 (False) | Skip | [2] |
| 2 | 3 | 3 | 2 | 3 > 2 (True) | Leader! right_most = 3 | [2, 3] |
| 3 | 2 | 5 | 3 | 5 > 3 (True) | Leader! right_most = 5 | [2, 3, 5] |
| 4 | 1 | 2 | 5 | 2 > 5 (False) | Skip | [2, 3, 5] |
| 5 | 0 | 1 | 5 | 1 > 5 (False) | Skip | [2, 3, 5] |
Reversing ans → [5, 3, 2]
Output: [5, 3, 2]
Python Solution
class Solution:
def leaders(self, nums: list) -> list:
ans = []
if len(nums) == 0: return ans
ans.append(nums[-1])
right_most = nums[-1]
index = len(nums) - 2
while index >= 0:
if nums[index] > right_most:
right_most = nums[index]
ans.append(right_most)
index -= 1
return ans[::-1]
# main function for the program
def main():
sol = Solution()
nums = [1, 2, 5, 3, 1, 2]
print(sol.leaders(nums))
# driver code for the program
if __name__ == '__main__':
main()
Complexity Analysis
-
Time Complexity: O(N)
- Backward scan visits each of the N elements once.
- Reversing the resulting
anslist takes O(K) time (where K <= N). - Total time complexity is O(N).
-
Space Complexity: O(1) auxiliary space (excluding the output array
ans).
3. Rearrange Array Elements by Sign
Problem Description
Given a 0-indexed integer array nums of even length containing an equal number of positive and negative integers, rearrange the elements of nums such that:
- Every consecutive pair of integers has alternating signs.
- For all integers with the same sign, the relative order in which they appeared in
numsis preserved. - The rearranged array begins with a positive integer (positive elements at even indices
0, 2, 4..., negative elements at odd indices1, 3, 5...).
Intuition & Two-Pointer Placement Approach
Since positive integers must end up at even indices (0, 2, 4...) and negative integers at odd indices (1, 3, 5...), we can populate a new result array ans of length N in a single pass:
- Initialize
pos_index = 0(points to the next available even index). - Initialize
neg_index = 1(points to the next available odd index). - Iterate through
nums:- If
el > 0: Assignans[pos_index] = eland advancepos_index += 2. - If
el < 0: Assignans[neg_index] = eland advanceneg_index += 2.
- If
- Return
ans.
Example Walkthrough
Input: nums = [2, 4, 5, -1, -3, -4]
| Step | Element el | Sign | Placed at ans[idx] | pos_index | neg_index | Result Array ans |
|---|---|---|---|---|---|---|
| 0 | 2 | Positive | ans[0] = 2 | 2 | 1 | [2, 0, 0, 0, 0, 0] |
| 1 | 4 | Positive | ans[2] = 4 | 4 | 1 | [2, 0, 4, 0, 0, 0] |
| 2 | 5 | Positive | ans[4] = 5 | 6 | 1 | [2, 0, 4, 0, 5, 0] |
| 3 | -1 | Negative | ans[1] = -1 | 6 | 3 | [2, -1, 4, 0, 5, 0] |
| 4 | -3 | Negative | ans[3] = -3 | 6 | 5 | [2, -1, 4, -3, 5, 0] |
| 5 | -4 | Negative | ans[5] = -4 | 6 | 7 | [2, -1, 4, -3, 5, -4] |
Output: [2, -1, 4, -3, 5, -4]
Python Solution
class Solution:
def rearrange_array(self, nums: list) -> list:
ans = [0] * len(nums)
pos_index, neg_index = 0, 1
for index, el in enumerate(nums):
if el < 0:
ans[neg_index] = el
neg_index += 2
else:
ans[pos_index] = el
pos_index += 2
return ans
# main function for the program
def main():
sol = Solution()
nums = [2, 4, 5, -1, -3, -4]
print(sol.rearrange_array(nums))
# driver code for the program
if __name__ == '__main__':
main()
Complexity Analysis
- Time Complexity: O(N) — Linear scan traversing the N elements once.
- Space Complexity: O(N) — Memory allocated for the output array
ansof size N.
4. Sum and Target Based Problems (Two Sum & Three Sum)
Problem Description
- Two Sum: Given an array
numsand atarget, return the 0-based indices of the two numbers that sum totarget. - Two Sum Exists: Given an array
numsand atarget, returnTrueif any pair sums totarget, elseFalse. - Three Sum: Given an integer array
nums, return all unique triplets[nums[i], nums[j], nums[k]]such that i != j != k and nums[i] + nums[j] + nums[k] = 0.
Intuition & Approach
Two Sum (Hash Map Approach)
Instead of a nested loop (O(N²)), use a hash map to record visited elements and their indices:
- For each element
elatindex, calculate complementrequired = target - el. - If
requiredexists inhash_map, return[hash_map[required], index]. - Otherwise, store
hash_map[el] = index.
Three Sum (Sorting + Two Pointers)
- Sort
numsin non-decreasing order (O(N log N)). - Iterate
ifrom0tolen(nums) - 3:- Skip duplicate values for
nums[i]to avoid duplicate triplets. - Set two pointers:
left = i + 1,right = len(nums) - 1. - While
left < right:- Calculate
s = nums[i] + nums[left] + nums[right]. - If
s == 0: Found a triplet! Append[nums[i], nums[left], nums[right]], then incrementleftand decrementrightwhile skipping duplicate values. - Else if
s < 0: Incrementleft += 1to increase the sum. - Else (
s > 0): Decrementright -= 1to decrease the sum.
- Calculate
- Skip duplicate values for
Example Walkthrough (Two Sum)
Input: nums = [1, 6, 2, 10, 3], target = 7
| Step | index | el | target - el | In hash_map? | Action | hash_map State |
|---|---|---|---|---|---|---|
| 0 | 0 | 1 | 6 | No | Store hash_map[1] = 0 | {1: 0} |
| 1 | 1 | 6 | 1 | Yes! (index 0) | Return [0, 1] | {1: 0} |
Output: [0, 1]
Python Solution
class Solution:
def two_sum(self, nums: list, target: int) -> list:
ans = [-1, -1]
hash_map = {}
for index, el in enumerate(nums):
if (target - el) in hash_map:
return [hash_map[target - el], index]
hash_map[el] = index
return ans
def two_sum_exists(self, nums: list, target: int) -> bool:
s = set()
for el in nums:
if target - el in s:
return True
s.add(el)
return False
def three_sum(self, nums: list) -> list:
nums.sort()
ans = []
n = len(nums)
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i - 1]:
continue
left, right = i + 1, n - 1
while left < right:
s = nums[i] + nums[left] + nums[right]
if s == 0:
ans.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1
while left < right and nums[left] == nums[left - 1]:
left += 1
while right > left and nums[right] == nums[right + 1]:
right -= 1
elif s < 0:
left += 1
else:
right -= 1
return ans
# main function for the program
def main():
sol = Solution()
nums = [1, 6, 2, 10, 3]
target = 7
print(sol.two_sum(nums, target))
# driver code for the program
if __name__ == '__main__':
main()
Complexity Analysis
-
Two Sum:
- Time Complexity: O(N) — Single pass using O(1) average hash map lookup.
- Space Complexity: O(N) — Space stored in
hash_map.
-
Three Sum:
- Time Complexity: O(N²) — Sorting takes O(N log N) and the nested two-pointer scan takes O(N²).
- Space Complexity: O(1) auxiliary space (excluding output list).
5. Pascal's Triangle Problems
Problem Description
Pascal's Triangle is a triangular array of numbers where each entry is the sum of the two numbers directly above it.
- Variation 1 (
pascal_triangle_one): Given 1-indexed rowrand columnc, return the value at element (r, c). - Variation 2 (
pascal_triangle_two): Given 1-indexed rowr, return all values in the r-th row. - Variation 3 (
pascal_triangle_three): Given integern, return the firstnrows of Pascal's Triangle.
Intuition & Mathematical Formula
Any element at row r and column c (1-indexed) in Pascal's Triangle corresponds to the mathematical combination: Element(r, c) = C(r - 1, c - 1) = ((r - 1)!) / ((c - 1)! × (r - c)!)
Approach
- Compute factorials fact(n) = n!.
- Compute combination C(r, c) = (r!) / (c! × (r - c)!).
- Use combination helper to calculate individual positions or build row vectors.
Example Walkthrough
Pascal's Triangle Structure:
Row 1: 1
Row 2: 1 1
Row 3: 1 2 1
Row 4: 1 3 3 1
pascal_triangle_one(4, 2)→ C(4-1, 2-1) = C(3, 1) = 3pascal_triangle_two(4)→ <=ft[C(3, 0), C(3, 1), C(3, 2), C(3, 3)] = [1, 3, 3, 1]pascal_triangle_three(4)→ [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1]]
Python Solution
class Solution:
def __factorial__(self, num: int) -> int:
fact = 1
for i in range(1, num + 1):
fact = fact * i
return fact
def __combination__(self, r: int, c: int) -> int:
return (
self.__factorial__(r) // (self.__factorial__(c) * self.__factorial__(r - c))
)
"""
Given two integers r and c.
Return the value at the rth row and cth column (1-indexed) in a Pascal's Triangle.
"""
def pascal_triangle_one(self, r: int, c: int) -> int:
return self.__combination__(r - 1, c - 1)
"""
Given an integer r, return all the values in the rth row (1-indexed) in Pascal's Triangle in correct order.
"""
def pascal_triangle_two(self, r: int) -> list:
ans = []
for i in range(r):
ans.append(self.__combination__(r - 1, i))
return ans
"""
Given an integer n, return the first n (1-Indexed) rows of Pascal's triangle.
"""
def pascal_triangle_three(self, n: int) -> list:
ans = []
if n == 0: return []
if n == 1: return [1]
if n == 2: return [[1], [1,1]]
for i in range(1, n + 1):
out = []
for j in range(i):
out.append(self.__combination__(i - 1, j))
ans.append(out)
return ans
# main function for the program
def main():
sol = Solution()
r = 4
c = 2
print(sol.pascal_triangle_one(r, c))
print(sol.pascal_triangle_two(4))
print(sol.pascal_triangle_three(4))
# driver code for the program
if __name__ == '__main__':
main()
Complexity Analysis
-
Time Complexity:
pascal_triangle_one: O(r) to compute factorials.pascal_triangle_two: O(r^2) computing combinations for row r.pascal_triangle_three: O(n^3) computing combinations for each entry up to row n.
-
Space Complexity: O(1) auxiliary space (excluding result lists).
6. Sort Zeroes, Ones, and Twos (Dutch National Flag Algorithm)
Problem Description
Given an array nums containing only 0s, 1s, and 2s, sort the array in-place so that all 0s come first, followed by all 1s, and then all 2s.
You must solve this problem in-place without using built-in sort functions.
Intuition & Dutch National Flag Algorithm
The Dutch National Flag Algorithm (formulated by Edsger Dijkstra) uses 3 pointers (left, mid, right) to partition the array into 4 zones in a single pass:
[0 ... left-1] --> All 0s
[left ... mid-1] --> All 1s
[mid ... right] --> Unprocessed / Unknown elements
[right+1 ... N-1]--> All 2s
Algorithm Steps:
- Initialize
left = 0,mid = 0,right = len(nums) - 1. - Loop while
mid <= right:- If
nums[mid] == 0: Swapnums[left]andnums[mid]. Incrementleft += 1andmid += 1. - If
nums[mid] == 1: Element is already in correct middle zone. Incrementmid += 1. - If
nums[mid] == 2: Swapnums[mid]andnums[right]. Decrementright -= 1(do not incrementmidyet, as the swapped element fromrightneeds inspection).
- If
Example Walkthrough
Input: nums = [1, 0, 2, 1, 0]
| Step | left | mid | right | nums[mid] | Action | Array State |
|---|---|---|---|---|---|---|
| Initial | 0 | 0 | 4 | 1 | nums[mid] == 1 → mid += 1 | [1, 0, 2, 1, 0] |
| 1 | 0 | 1 | 4 | 0 | nums[mid] == 0 → Swap nums[0], nums[1], left=1, mid=2 | [0, 1, 2, 1, 0] |
| 2 | 1 | 2 | 4 | 2 | nums[mid] == 2 → Swap nums[2], nums[4], right=3 | [0, 1, 0, 1, 2] |
| 3 | 1 | 2 | 3 | 0 | nums[mid] == 0 → Swap nums[1], nums[2], left=2, mid=3 | [0, 0, 1, 1, 2] |
| 4 | 2 | 3 | 3 | 1 | nums[mid] == 1 → mid += 1 (mid=4) | [0, 0, 1, 1, 2] |
mid = 4 > right = 3 → Loop terminates.
Output: [0, 0, 1, 1, 2]
Python Solution
class Solution:
def sort_zero_one_two(self, nums: list) -> None:
left, right = 0, len(nums) - 1
mid = 0
while mid <= right:
if nums[mid] == 0:
nums[left], nums[mid] = nums[mid], nums[left]
left += 1
mid += 1
elif nums[mid] == 2:
nums[mid], nums[right] = nums[right], nums[mid]
right -= 1
else:
mid += 1
# main function for the program
def main():
sol = Solution()
nums = [1, 0, 2, 1, 0]
sol.sort_zero_one_two(nums)
print(nums)
# driver code for the program
if __name__ == '__main__':
main()
7. Maximum Subarray Sum (Kadane's Algorithm)
Problem Description
Given an integer array nums, find the contiguous subarray (containing at least one element) which has the largest sum and return its maximum sum.
Intuition & Kadane's Algorithm
1. Naive / Brute Force Approach (O(N²))
Check all possible contiguous subarrays nums[i...j] using nested loops, compute their sums, and record the maximum.
2. Kadane's Algorithm (O(N) Single Pass)
Kadane's Algorithm dynamic programming approach optimizes this to linear time:
- Key Insight: If a running sum of a contiguous subarray becomes negative, carrying it forward into future elements will only reduce their total sum. Therefore, whenever the cumulative sum
cont_sumdrops below0, we resetcont_sum = 0to start a fresh candidate subarray from the next element.
Algorithm Steps:
- Initialize
max_sum = -float("inf")(to handle all-negative arrays correctly) andcont_sum = 0. - Iterate through each element
elinnums:- Add
eltocont_sum:cont_sum += el. - Update
max_sum:max_sum = max(max_sum, cont_sum). - If
cont_sum < 0: Resetcont_sum = 0(discard negative prefix).
- Add
- Return
max_sum.
Example Walkthrough
Input: nums = [-2, -3, -7, -2, -10, -4]
(All-negative array case)
| Step | Index | Element el | cont_sum (before reset) | max_sum state | cont_sum < 0 Action | cont_sum (after) |
|---|---|---|---|---|---|---|
| 0 | 0 | -2 | -2 | max(-inf, -2) = -2 | -2 < 0 → Reset | 0 |
| 1 | 1 | -3 | -3 | max(-2, -3) = -2 | -3 < 0 → Reset | 0 |
| 2 | 2 | -7 | -7 | max(-2, -7) = -2 | -7 < 0 → Reset | 0 |
| 3 | 3 | -2 | -2 | max(-2, -2) = -2 | -2 < 0 → Reset | 0 |
| 4 | 4 | -10 | -10 | max(-2, -10) = -2 | -10 < 0 → Reset | 0 |
| 5 | 5 | -4 | -4 | max(-2, -4) = -2 | -4 < 0 → Reset | 0 |
Output: -2
Python Solution
class Solution:
def max_sub_array(self, nums: list) -> int:
max_sum = -float("inf")
cont_sum = 0
for index, el in enumerate(nums):
cont_sum += el
max_sum = max(max_sum, cont_sum)
if cont_sum < 0:
cont_sum = 0
return max_sum
# main function for the program
def main():
sol = Solution()
nums = [-2, -3, -7, -2, -10, -4]
print(sol.max_sub_array(nums))
# driver code for the program
if __name__ == '__main__':
main()
Complexity Analysis
- Time Complexity: O(N) — Single pass linear scan traversing the N elements once.
- Space Complexity: O(1) — Constant auxiliary space using scalar variables (
max_sumandcont_sum).