Array · 36 of 36
Median of 2 sorted arrays of different size
Two sorted lines of kids stand side by side, and you find the middle height of everyone together without merging the lines, by guessing where to cut each line.
- Simple way
- time O(m + n)
room O(m + n) - Clever way
- time O(log(min(m, n)))
room O(1)
The problem, simply
You get two sorted arrays, A with m numbers and B with n numbers. The sizes can be different, and one can even be empty. Imagine pouring them together into one sorted array. Give back its median: the middle number if the total is odd, or the average of the two middle numbers if the total is even.
The famous version asks you to do it in O(log(m + n)) time, so you may not really pour them together.
A story to remember it
Picture it
A has 5 numbers and B has 6. Together that is 11, so the median is the 6th smallest. Poured together it would be the green 11, but we want to find it without pouring.
- answer
Examples
Odd total
- In
A = [1, 3], B = [2]- Out
2.0
Together: 1, 2, 3. The middle is 2.
Even total
- In
A = [1, 2], B = [3, 4]- Out
2.5
Together: 1, 2, 3, 4. The two middle numbers are 2 and 3, and their average is 2.5.
A normal case
- In
A = [1, 3, 8, 9, 15], B = [7, 11, 18, 19, 21, 25]- Out
11.0
Eleven numbers in all, so the 6th smallest is the median: 1, 3, 7, 8, 9, 11.
One array is empty
- In
A = [], B = [4]- Out
4.0
Only B counts. Your code must not read A[0].
No overlap at all
- In
A = [2, 3, 5, 8], B = [10, 12, 14, 16, 18, 20]- Out
11.0
All of A is smaller than all of B. The cut in A ends up after its last number, where there is nothing on the right.
Way 1: the simple way
Pour both arrays into one with two fingers, like merging two sorted piles of cards. Then read the middle.
- Put a finger at the start of
Aand ofB. - Copy the smaller of the two numbers under your fingers into
merged, and move that finger. - When one array runs out, copy the rest of the other.
- Read the middle one or two numbers of
merged.
class Solution {
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length, n = B.length;
int[] merged = new int[m + n];
int i = 0, j = 0, k = 0;
while (i < m && j < n) merged[k++] = (A[i] <= B[j]) ? A[i++] : B[j++];
while (i < m) merged[k++] = A[i++];
while (j < n) merged[k++] = B[j++];
int total = m + n;
if (total % 2 == 1) return merged[total / 2];
return (merged[total / 2 - 1] + merged[total / 2]) / 2.0;
}
}How long it takes: every number is copied once, O(m + n). Extra room: the merged array, O(m + n). You can drop the array by only counting up to the middle and remembering the last two numbers, which makes the room O(1), but the time is still O(m + n).
Way 2: the clever way
Do not pour. Cut each array into a left part and a right part.
- Make sure
Ais the shorter one (swap if needed). We will search over cuts inAonly. - The left side must hold
half = (m + n + 1) / 2numbers. IfAgivescutAnumbers, thenBmust givecutB = half - cutA. - Look at the four numbers around the cuts:
leftA,rightA,leftB,rightB. A missing number past an end counts as minus infinity on the left or plus infinity on the right. - The cut is right when
leftA <= rightBandleftB <= rightA. Then every left number is at most every right number. - If
leftA > rightB, A gave too many: move the cut inAto the left. Otherwise A gave too few: move it to the right. - When it is right: for an odd total, the median is
max(leftA, leftB). For an even total, it is the average ofmax(leftA, leftB)andmin(rightA, rightB).
Each guess halves the choices for cutA, which is binary search. Blue boxes below are on the left side of a cut. Dashed boxes are the four border numbers being checked, and a red box is the border that broke the rule.
Guess the cut, check the borders, halve
01 / 08
A has 5 numbers, B has 6. Total 11, so the left side must hold half = (11 + 1) / 2 = 6 numbers. cutA can be anything from 0 to 5.
- lo
- 0
- hi
- 5
- half
- 6
- checking
- answer
- rejected
- path
class Solution {
public double findMedianSortedArrays(int[] A, int[] B) {
if (A.length > B.length) return findMedianSortedArrays(B, A); // search the shorter one
int m = A.length, n = B.length;
int half = (m + n + 1) / 2; // how many numbers go on the left
int lo = 0, hi = m;
while (lo <= hi) {
int cutA = (lo + hi) / 2;
int cutB = half - cutA;
int leftA = (cutA == 0) ? Integer.MIN_VALUE : A[cutA - 1];
int rightA = (cutA == m) ? Integer.MAX_VALUE : A[cutA];
int leftB = (cutB == 0) ? Integer.MIN_VALUE : B[cutB - 1];
int rightB = (cutB == n) ? Integer.MAX_VALUE : B[cutB];
if (leftA <= rightB && leftB <= rightA) { // the cut is right
if ((m + n) % 2 == 1) return Math.max(leftA, leftB);
return (Math.max(leftA, leftB) + (double) Math.min(rightA, rightB)) / 2.0;
} else if (leftA > rightB) {
hi = cutA - 1; // A gave too many
} else {
lo = cutA + 1; // A gave too few
}
}
throw new IllegalArgumentException("arrays were not sorted");
}
}How long it takes: binary search over the cuts of the shorter array, O(log(min(m, n))). Extra room: a few numbers, O(1).
Watch out for
- Use
(m + n + 1) / 2for the left size. Then for an odd total, the median sits on the left side, andmax(leftA, leftB)is the answer. - An empty array is fine:
m = 0meanscutAis always 0 and the infinities do the rest. - Adding two big
intvalues can overflow. Cast one todouble(orlong) before adding, as in the code. - Return a
double. Integer division turns 2.5 into 2.
What interviewers ask next
- Find the k-th smallest of two sorted arrays. Same cut idea with
halfreplaced byk, or compareA[k/2 - 1]withB[k/2 - 1]and throw awayk/2numbers each step,O(log k). - Why is the answer
O(log(min(m, n)))and notO(log(m + n))? We only search the shorter array, so it is even better than asked. - What if the arrays are equal in size? The same code works. There is also an older trick that compares the two medians; see the equal size problem.
- Median of a stream of numbers? That is a different tool: two heaps, one for the smaller half and one for the bigger half.
Remember this
Grown-up words: the cut is a partition, the left side is the lower half of the merged order, minus and plus infinity are sentinel values, and guessing then halving is binary search on the partition index.