- Published on
Complete Solutions to Classic Searching Algorithm Problems
- Complete Solutions to Classic Searching Algorithm Problems
Complete Solutions to Classic Searching Algorithm Problems
This document provides comprehensive solutions to 9 fundamental searching algorithm problems commonly asked in technical interviews. Each solution includes detailed explanations, Go implementations, time/space complexity analysis, and key insights.
🎯 Problem Categories
- Easy (3 problems): Basic binary search applications
- Medium (3 problems): Advanced binary search with modifications
- Hard (3 problems): Complex scenarios requiring deep understanding
🟢 Easy Problems
These problems focus on fundamental binary search concepts and are great starting points for interview preparation.
1. Search Insert Position (LeetCode 35)
Problem: Given a sorted array and a target value, return the index where the target should be inserted to maintain sorted order.
Key Insight: Use binary search but return left
pointer when not found - this gives the insertion position.
Algorithm:
- Standard binary search with modification
- When loop ends,
left
points to insertion position - Works for duplicates and edge cases
func searchInsert(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return mid
} else if nums[mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return left // Insertion position
}
Time Complexity: O(log n)
Space Complexity: O(1)
Example: [1,3,5,6], target=5
→ return 2
2. First Bad Version (LeetCode 278)
Problem: Find the first bad version in a sequence where all versions after the first bad one are also bad.
Key Insight: This is a "find first occurrence" binary search variant. We need to find the leftmost true
in a sequence of false...false,true...true
.
Algorithm:
- Use
left < right
(not<=
) to avoid infinite loop - When
isBadVersion(mid)
is true, search left half (including mid) - When false, search right half (excluding mid)
func firstBadVersion(n int, isBadVersion func(int) bool) int {
left, right := 1, n
for left < right {
mid := left + (right-left)/2
if isBadVersion(mid) {
right = mid
} else {
left = mid + 1
}
}
return left // First bad version
}
Time Complexity: O(log n)
Space Complexity: O(1)
Example: n=5, bad starts at 4
→ return 4
3. Square Root using Binary Search (LeetCode 69)
Problem: Compute and return the integer part of the square root of a non-negative integer x.
Key Insight: Binary search on the answer space [1, x/2]. We want the largest number whose square ≤ x.
Algorithm:
- Handle edge cases: x < 2
- Search space is [1, x/2] since sqrt(x) ≤ x/2 for x ≥ 4
- Return
right
(last valid answer) when loop ends
func mySqrt(x int) int {
if x < 2 {
return x
}
left, right := 1, x/2
for left <= right {
mid := left + (right-left)/2
if mid*mid == x {
return mid
} else if mid*mid < x {
left = mid + 1
} else {
right = mid - 1
}
}
return right // Largest integer whose square ≤ x
}
Time Complexity: O(log x)
Space Complexity: O(1)
Example: x=8
→ return 2
(since 2² = 4 ≤ 8 < 9 = 3²)
🟡 Medium Problems
These problems require modifications to standard binary search and handling of special cases.
1. Search in Rotated Sorted Array (LeetCode 33)
Problem: Search for a target in a rotated sorted array with distinct elements.
What is a Rotated Sorted Array?
A rotated sorted array is an array that was originally sorted in ascending order, but then some leading elements were moved to the end. For example, [0,1,2,4,5,6,7]
rotated at index 3 becomes [4,5,6,7,0,1,2]
. The array remains sorted except for a "pivot" point where the rotation happened. This property allows us to use a modified binary search to efficiently find elements.
Key Insight: At least one half is always sorted. Determine which half is sorted, then check if target lies in that half.
Algorithm:
- Compare
nums[left]
withnums[mid]
to find sorted half - If left half sorted: check if target in range [nums[left], nums[mid])
- If right half sorted: check if target in range (nums[mid], nums[right]]
- Search the appropriate half
func search(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return mid
}
if nums[left] <= nums[mid] {
if nums[left] <= target && target < nums[mid] {
right = mid - 1
} else {
left = mid + 1
}
} else {
if nums[mid] < target && target <= nums[right] {
left = mid + 1
} else {
right = mid - 1
}
}
}
return -1 // Target not found
}
Time Complexity: O(log n)
Space Complexity: O(1)
Example: [4,5,6,7,0,1,2], target=0
→ return 4
2. Find First and Last Position of Element (LeetCode 34)
Problem: Find the starting and ending position of a target value in a sorted array.
Key Insight: Use two separate binary searches - one for first occurrence, one for last occurrence.
Algorithm:
findFirst
: When target found, continue searching left (right = mid - 1)findLast
: When target found, continue searching right (left = mid + 1)- Return [-1, -1] if target not found
func searchRange(nums []int, target int) []int {
return []int{findFirst(nums, target), findLast(nums, target)}
}
func findFirst(nums []int, target int) int {
res, left, right := -1, 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
res = mid
right = mid - 1 // Continue searching left
} else if nums[mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return res
}
func findLast(nums []int, target int) int {
res, left, right := -1, 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
res = mid
left = mid + 1 // Continue searching right
} else if nums[mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return res
}
Time Complexity: O(log n) - two binary searches
Space Complexity: O(1)
Example: [5,7,7,8,8,10], target=8
→ return [3,4]
3. Search a 2D Matrix (LeetCode 74)
Problem: Search for a target in an m×n matrix where each row is sorted and the first element of each row is greater than the last element of the previous row.
Key Insight: Treat the 2D matrix as a 1D sorted array. Use index mapping: matrix[mid/n][mid%n]
.
Algorithm:
- Convert 2D coordinates to 1D:
index = row * n + col
- Convert 1D back to 2D:
row = index / n, col = index % n
- Apply standard binary search on virtual 1D array
func searchMatrix(matrix [][]int, target int) bool {
if len(matrix) == 0 || len(matrix[0]) == 0 {
return false
}
m, n := len(matrix), len(matrix[0])
left, right := 0, m*n-1
for left <= right {
mid := left + (right-left)/2
val := matrix[mid/n][mid%n]
if val == target {
return true
} else if val < target {
left = mid + 1
} else {
right = mid - 1
}
}
return false // Target not found
}
Time Complexity: O(log(m×n))
Space Complexity: O(1)
Example: [[1,4,7],[2,5,8],[3,6,9]], target=5
→ return true
🔴 Hard Problems
These problems require advanced techniques and deep understanding of binary search principles.
1. Median of Two Sorted Arrays (LeetCode 4)
Problem: Find the median of two sorted arrays in O(log(min(m,n))) time.
Key Insight: Use binary search to partition both arrays such that all elements on the left are ≤ all elements on the right.
Algorithm:
- Ensure nums1 is smaller array for optimization
- Binary search on nums1 to find correct partition
- Partition condition:
nums1[i-1] ≤ nums2[j]
andnums2[j-1] ≤ nums1[i]
- Calculate median from max(left) and min(right)
func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
if len(nums1) > len(nums2) {
return findMedianSortedArrays(nums2, nums1)
}
m, n := len(nums1), len(nums2)
imin, imax, halfLen := 0, m, (m+n+1)/2
for imin <= imax {
i := (imin + imax) / 2
j := halfLen - i
if i < m && nums2[j-1] > nums1[i] {
imin = i + 1
} else if i > 0 && nums1[i-1] > nums2[j] {
imax = i - 1
} else {
var maxLeft int
if i == 0 {
maxLeft = nums2[j-1]
} else if j == 0 {
maxLeft = nums1[i-1]
} else if nums1[i-1] > nums2[j-1] {
maxLeft = nums1[i-1]
} else {
maxLeft = nums2[j-1]
}
if (m+n)%2 == 1 {
return float64(maxLeft)
}
var minRight int
if i == m {
minRight = nums2[j]
} else if j == n {
minRight = nums1[i]
} else if nums1[i] < nums2[j] {
minRight = nums1[i]
} else {
minRight = nums2[j]
}
return float64(maxLeft+minRight) / 2.0
}
}
return 0.0
}
Time Complexity: O(log(min(m,n)))
Space Complexity: O(1)
Example: [1,3], [2]
→ return 2.0
(median of [1,2,3])
2. Find Minimum in Rotated Sorted Array II (LeetCode 154)
Problem: Find the minimum element in a rotated sorted array that may contain duplicates.
Key Insight: Handle duplicates by shrinking search space when nums[mid] == nums[right]
. This is the key difference from version I.
Algorithm:
- If
nums[mid] > nums[right]
: minimum is in right half - If
nums[mid] < nums[right]
: minimum is in left half (including mid) - If
nums[mid] == nums[right]
: can't determine, shrink right boundary
func findMin(nums []int) int {
left, right := 0, len(nums)-1
for left < right {
mid := left + (right-left)/2
if nums[mid] > nums[right] {
left = mid + 1
} else if nums[mid] < nums[right] {
right = mid
} else {
right-- // Handle duplicates
}
}
return nums[left] // Minimum element
}
Time Complexity: O(log n) average, O(n) worst case (all duplicates)
Space Complexity: O(1)
Example: [2,2,2,0,1]
→ return 0
3. Search in Rotated Sorted Array II (LeetCode 81)
Problem: Search for a target in a rotated sorted array that may contain duplicates.
Key Insight: Handle the case where nums[left] == nums[mid] == nums[right]
by shrinking both boundaries, as we can't determine which half is sorted.
Algorithm:
- Handle duplicates: if all three equal, shrink both boundaries
- Determine sorted half (similar to version I)
- Check if target is in the sorted half's range
- Search appropriate half
func search(nums []int, target int) bool {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return true
}
if nums[left] == nums[mid] && nums[mid] == nums[right] {
left++
right--
} else if nums[left] <= nums[mid] {
if nums[left] <= target && target < nums[mid] {
right = mid - 1
} else {
left = mid + 1
}
} else {
if nums[mid] < target && target <= nums[right] {
left = mid + 1
} else {
right = mid - 1
}
}
}
return false // Target not found
}
Time Complexity: O(log n) average, O(n) worst case (all duplicates)
Space Complexity: O(1)
Example: [2,5,6,0,0,1,2], target=0
→ return true
📚 Summary
These 9 problems cover the essential patterns in searching algorithms:
Key Patterns Learned:
- Standard Binary Search - Search Insert Position, Square Root
- Find Boundary - First Bad Version, Find First/Last Position
- Modified Arrays - Rotated arrays, 2D matrix
- Advanced Techniques - Partitioning, duplicate handling
Interview Tips:
- Always clarify array properties (sorted, duplicates, etc.)
- Handle edge cases (empty arrays, single elements)
- Watch for integer overflow in mid calculation
- Practice drawing the search space and elimination process
Time Complexity Summary:
- Most problems: O(log n)
- With duplicates: O(n) worst case
- 2D matrix: O(log(m×n))
- Median of two arrays: O(log(min(m,n)))
Master these patterns and you'll be well-prepared for any searching algorithm interview question! 🚀