There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
You may assume nums1 and nums2 cannot be both empty.
Example 1:
nums1 = [1, 3] nums2 = [2] The median is 2.0Example 2:
nums1 = [1, 2] nums2 = [3, 4] The median is (2 + 3)/2 = 2.5
寻找两个正序数组的中位数。这个题是有时间复杂度的要求的,O(log (m+n))。如果没有这个要求,因为两个数组都是各自有序的,所以就可以把两个数组合并然后找中位数就行了。我这里给出最优解,思路参见这个帖子。时间复杂度更好,是O(log(min(m, n)))。
既然是找两个数组的中位数,让我们先回到比较直观的做法。我们可以先将两个数组合并,根据合并后的数组长度(很容易知道)返回中间的那个元素或者中间两个元素的平均值(这个也很容易知道)。如果只是一般情况,那么就是nums1里面拿出来几个元素,nums2里面也拿出来几个元素凑成合并数组的左半边,同样的,合并数组的右半边也会是这样组成的。如果我知道nums1里面拿出来几个元素就能凑成合并数组的左半边的话,我同样也就能知道nums2需要贡献几个元素。为什么呢?因为中位数的index = (len1 + len2) / 2。nums2需要贡献的元素个数是index - nums1贡献的元素个数。所以这个题的时间复杂度可以被控制在O(log(min(m, n))),找到短的那个数组的切分位置,你也就找到了另一个数组的切分位置。
找到切分位置的时候,如何这一刀切分的对不对呢?可以记录四个元素L1, R1, L2, R2分别表示在数组1和数组2的切分位置左边和右边的元素都是什么。如果这一刀的位置是正确的,则应该有的结果是
- L1<=R2
- L2<=R1
这样就能确保,左边的元素都小于右边的元素了。下图帮助理解。
时间O(log(min(m, n)))
空间O(1)
Java实现
1 class Solution { 2 public double findMedianSortedArrays(int[] nums1, int[] nums2) { 3 int lenA = nums1.length; 4 int lenB = nums2.length; 5 // make sure the first array is shorter 6 if (lenA > lenB) { 7 return findMedianSortedArrays(nums2, nums1); 8 } 9 10 // 如果一个数组为空,直接计算另一个数组的中位数 11 if (lenA == 0) { 12 return ((double) nums2[(lenB - 1) / 2] + (double) nums2[lenB / 2]) / 2; 13 } 14 15 // normal case 16 int len = lenA + lenB; 17 // 对nums1做二分的范围 18 int aLeft = 0; 19 int aRight = lenA; 20 int cutA; 21 int cutB; 22 while (aLeft <= aRight) { 23 cutA = aLeft + (aRight - aLeft) / 2; 24 cutB = (len + 1) / 2 - cutA; 25 double L1 = (cutA == 0) ? Integer.MIN_VALUE : nums1[cutA - 1]; 26 double R1 = (cutA == lenA) ? Integer.MAX_VALUE : nums1[cutA]; 27 double L2 = (cutB == 0) ? Integer.MIN_VALUE : nums2[cutB - 1]; 28 double R2 = (cutB == lenB) ? Integer.MAX_VALUE : nums2[cutB]; 29 if (L1 > R2) { 30 aRight = cutA - 1; 31 } else if (L2 > R1) { 32 aLeft = cutA + 1; 33 } else { 34 if (len % 2 == 0) { 35 return (Math.max(L1, L2) + Math.min(R1, R2)) / 2; 36 } else { 37 return Math.max(L1, L2); 38 } 39 } 40 } 41 return -1; 42 } 43 }
JavaScript实现
1 /** 2 * @param {number[]} nums1 3 * @param {number[]} nums2 4 * @return {number} 5 */ 6 var findMedianSortedArrays = function(nums1, nums2) { 7 /* 8 nums3 => nums1 + nums2 => nums1 9 只需考慮 nums1 為最短邊,則計算最少 10 */ 11 if (nums1.length > nums2.length) { 12 return findMedianSortedArrays(nums2, nums1); 13 } 14 let len = nums1.length + nums2.length; 15 let cut1 = 0; 16 let cut2 = 0; 17 let cutL = 0; 18 let cutR = nums1.length; 19 while (cut1 <= nums1.length) { 20 /* 21 * L1 R1 22 * (cut1)-1 cut1 23 * nums1 = 3 5 | 8 9 24 * 25 * L2 R2 26 * (cut2)-1 cut2 27 * nums2 = 1 2 7 | 10 11 12 28 * 29 * L1 + L2 = L3 R3 = R1 + R2 30 * nums3 = 1 2 3 5 7 | 8 9 10 11 12 31 * 32 * cut 1 為 L3 提供 [3, 5] 所以 cut2 則提供 5 - 2 = 3([1, 2, 7]) 元素 33 */ 34 cut1 = parseInt(cutL + (cutR - cutL) / 2); 35 cut2 = parseInt((len / 2) - cut1); 36 // 不可使用 MAX_VALUE 要用 Number.MIN_SAFE_INTEGER,否則會超過時間 37 // 若超出界線 則使用最小值 38 let L1 = (cut1 === 0) ? Number.MIN_SAFE_INTEGER : nums1[cut1-1]; 39 let L2 = (cut2 === 0) ? Number.MIN_SAFE_INTEGER : nums2[cut2-1]; 40 // 不可使用 MIN_VALUE 要用 Number.MIN_SAFE_INTEGER,否則會超過時間 41 // 若超出界線 則使用最大值 42 let R1 = (cut1 === nums1.length) ? Number.MAX_SAFE_INTEGER : nums1[cut1]; 43 let R2 = (cut2 === nums2.length) ? Number.MAX_SAFE_INTEGER : nums2[cut2]; 44 /* 45 假如情況為: 46 nums1 = 3 5 8 | 9 (<<) 47 nums2 = 1 2 | 7 11 12 48 8 > 7 49 則 nums1 必須 在往上一格移動 << 所以為 (cut1 - 1) 50 */ 51 if(L1 > R2) { 52 cutR = cut1 - 1; 53 /* 54 假如情況為: 55 nums1 = 3 | 5 8 9 (>>) 56 nums2 = 1 2 7 | 11 12 57 7 > 5 58 則 nums1 必須 在往下一格移動 >> 所以為 (cut1 + 1) 59 */ 60 } else if (L2 > R1) { 61 cutL = cut1 + 1; 62 /* 63 滿足 以下條件 64 R1 >= L2 65 R2 >= L1 66 */ 67 } else { 68 // 假如 nums3 為偶數 69 // 如果是偶數 L 選擇最大的部分, R 選擇最小的部分 除以 2 70 /* 71 * L1 + L2 = L3 R3 = R1 + R2 72 * nums3 = 1 2 3 5 7 | 8 9 10 11 12 73 * */ 74 if ((len % 2) === 0) { 75 L1 = (L1 > L2) ? L1 : L2; 76 R1 = (R1 < R2) ? R1 : R2; 77 return (L1+R1) / 2; 78 } else{ 79 // 假如 nums3 為奇數 80 /* nums1 = 3 5 8 | 9 15 81 * nums2 = 1 2 7 | 10 11 12 82 * 如果是奇數選小的 83 * nums3 = 1 2 3 5 7 8 | 9 10 11 12 15 84 * */ 85 console.log(R1, R2); 86 R1 = (R1 < R2) ? R1 : R2; 87 return R1; 88 } 89 } 90 } 91 return -1; 92 }; 93 console.log(findMedianSortedArrays([3, 5, 8, 9], [1 , 2, 7, 10, 11, 12]))
我在二刷的时候看到另一个讲解,也非常好。思路和时间复杂度是一样的。
Java实现
1 class Solution { 2 public double findMedianSortedArrays(int[] nums1, int[] nums2) { 3 // corner case 4 if (nums1.length > nums2.length) { 5 int[] temp = nums1; 6 nums1 = nums2; 7 nums2 = temp; 8 } 9 10 // normal case 11 int m = nums1.length; 12 int n = nums2.length; 13 // 在短的数组里面进行二分法 14 int left = 0; 15 int right = m; 16 while (left <= right) { 17 int i = left + (right - left) / 2; 18 int j = (m + n + 1) / 2 - i; 19 int nums1LeftMax = i == 0 ? Integer.MIN_VALUE : nums1[i - 1]; 20 int nums1RightMin = i == m ? Integer.MAX_VALUE : nums1[i]; 21 int nums2LeftMax = j == 0 ? Integer.MIN_VALUE : nums2[j - 1]; 22 int nums2RightMin = j == n ? Integer.MAX_VALUE : nums2[j]; 23 if (nums1LeftMax <= nums2RightMin && nums2LeftMax <= nums1RightMin) { 24 if (((m + n) & 1) == 1) { 25 return Math.max(nums1LeftMax, nums2LeftMax); 26 } else { 27 return (double) ((Math.max(nums1LeftMax, nums2LeftMax) + Math.min(nums1RightMin, nums2RightMin))) 28 / 2; 29 } 30 } else if (nums2LeftMax > nums1RightMin) { 31 left = i + 1; 32 } else { 33 right = i - 1; 34 } 35 } 36 return -1; 37 } 38 }