Median of Two Sorted Arrays
Problem
You are given two integer arrays nums1 and nums2 of size m and n respectively, where each is sorted in ascending order. Return the median value among all elements of the two arrays.
Your solution must run in O(log(m+n)) time.
Examples
Example 1:
Input: nums1 = [1,2], nums2 = [3]
Output: 2.0
Explanation: Among [1, 2, 3] the median is 2.
Example 2:
Input: nums1 = [1,3], nums2 = [2,4]
Output: 2.5
Explanation: Among [1, 2, 3, 4] the median is (2 + 3) / 2 = 2.5.
Constraints
nums1.length == mnums2.length == n0 <= m <= 10000 <= n <= 1000-10^6 <= nums1[i], nums2[i] <= 10^6
You should aim for a solution with O(log(min(n, m))) time and O(1) space, where n is the size of nums1 and m is the size of nums2.
Solution
Idea is to use the fact that we know how many elements would be on the left side of the merged array. We perform binary search on the smaller of the two arrays in terms of how many elements to select from it. If we take all the elements from A, then we know the solution lies left_size - M within B, which is given by j.
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
constexpr auto INF = std::numeric_limits<int>::max();
constexpr auto NINF = std::numeric_limits<int>::lowest();
// Binary search on the smaller of the two vectors
if (nums1.size() > nums2.size()) {
return findMedianSortedArrays(nums2, nums1);
}
const std::vector<int> &A = nums1;
const std::vector<int> &B = nums2;
int M = A.size();
int N = B.size();
// We want to simulate partitioning the merged array
// The left partition will have half the elements (+1 if odd length)
int left_size = (M + N + 1) / 2;
// Binary search is over how many elements we take from A
// i = number of elements from A placed on the left side
// j = number of elements from B placed on the left side
int low = 0;
int high = M;
while (low <= high) {
// Left side contains left_size elements, therefore j = left_size - i
int i = (low + high) / 2;
int j = left_size - i;
// Partitions:
// A: [A[0], ..., A[i-1] | A[i], ..., A[M-1]]
// B: [B[0], ..., B[j-1] | B[j], ..., B[N-1]]
// If i = 0, A contributes nothing to the left side
// If i = M, A contributes everything to the left side
int leftA = (i == 0) ? NINF : A[i - 1];
int rightA = (i == M) ? INF : A[i];
int leftB = (j == 0) ? NINF : B[j - 1];
int rightB = (j == N) ? INF : B[j];
// We found the correct partition if everything on left is
// <= everything on the right
// Since A/B are each sorted, we only need to check the crossing boundaries
if (leftA <= rightB && leftB <= rightA) {
auto leftMax = std::max(leftA, leftB);
auto rightMin = std::min(rightA, rightB);
// Odd length, since left has one more element,
// median is largest element on the left
if ((M + N) % 2 == 1) { return static_cast<double>(leftMax); }
// Even length, median is average of largest element on left
// and smallest element on right
return static_cast<double>(leftMax + rightMin) / 2.0;
}
// If leftA > rightB, we took too many elements from A, so move i left
// Otherwise, we took too few elements from A, so move i right
if (leftA > rightB) { high = i - 1;}
else { low = i + 1; }
}
return 0;
}
};