0x00
难度:Hard
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.0
Example 2:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5
题目的意思是寻找中位数的平均值。题目的前提条件有问题,会对解题造成一定困扰,时间复杂度应该是 O(log(min(m,n))),不是 O(log(m+n))。
0x01
先说思路
- 找中位数可以转换成另一个命题,找一个数,其下标等于中位数的下标,也就是说这道题目可以转换成在整体数组中找某个Index的数。
- 假设存在两个单调递增的子数组 num1 与 num2。
- .假设我们需要找的数的下标为idx。我们判断num1中下标为num1Idx的数是否为整体数组中下标为idx的数,需要满足这个条件:
num2[idx - num1Idx - 1] <= num1[num1Idx] <= num2[idx-num1Idx]
。如果这个条件满足,那么num[num1Idx]恰好就是整体数组中下标为idx的数。 - 同理,如果满足
num1[num1Idx] <= num2[idx - num1Idx - 1] <= num1[num1Idx+1]
,就说明 num2[idx - num1Idx - 1]为整体数组中下标为idx的数。 - 那么我们就需要遍历num1,来判断是否满足这个条件。但是这个遍历也是有技巧的。我们取num1[num1Idx](n)与num2[idx - num1Idx - 1](m)比较大小。如果 n >= m,那么判断下步骤3是否满足,如果满足,遍历结束;如果不满足,我们就需要向下遍历num1。同理,如果n<=m,判断步骤4是否满足,如果不满足,我们就需要向上遍历num1。如此我们可以使用二分查找快速遍历。
- 我们在步骤3与4中,使用到了 idx - num1Idx - 1,这里就必须保证
idx - num1Idx - 1 >= 0
。也就是idx >= num1Idx +1
。因为使用二分查找,所以num1Idx=len(num1)/2
。而查找中位数的话,idx=(len(num1)+len(num2))/2
,因此就存在len(num1)<=len(num2)
,也就是时间复杂度为 O(log(min(m,n)))。
0x02
我的代码如下:
func checkMatch(num1,num2 []int,num1Idx int,idx int) (ret,valueGot int) {
if idx > len(num2) {
ret = 1
return//upper
}
if num2[idx] >= num1[num1Idx] {
//num1[num1Idx] <= num2[idx] <= num1[num1Idx+1]
if (num1Idx == len(num1) - 1) || (num2[idx] <= num1[num1Idx+1]) {
ret = 0
valueGot = num2[idx]
return
}
ret = 1
return
}
if num2[idx] <= num1[num1Idx]{
//num2[idx] <= num1[num1Idx] <= num2[idx+1]
if (idx == len(num2) - 1) || (num1[num1Idx] <= num2[idx+1]) {
ret = 0
valueGot = num1[num1Idx]
return
}
ret = -1
return
}
return
}
func locateValueByIdx(num1 ,num2 []int,idx int) int {
if 0 == len(num1) {
return num2[idx]
} else if 0 == len(num2) {
return num1[idx]
} else if 0 == idx {
if num1[0] > num2[0] {
return num2[0]
}
return num1[0]
}
if len(num2) > len(num1) {
num1,num2 = num2,num1
}
lIdx,hIdx := 0,len(num1) - 1
for {
midIdx := (lIdx + hIdx) / 2
if (midIdx + 1 > idx) || (idx - midIdx - 1 >= len(num2)) {
num1,num2 = num2,num1
lIdx,hIdx = 0,len(num1) - 1
continue
}
ret,valueGot := checkMatch(num1,num2,midIdx,idx - midIdx - 1)
if 0 == ret {
return valueGot
}
if 1 == ret {
lIdx = midIdx + 1
} else {
hIdx = midIdx - 1
}
if lIdx > hIdx {
return num2[idx]
}
}
}
func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
numLen := len(nums1) + len(nums2)
idx := numLen / 2
val1 := float64(locateValueByIdx(nums1,nums2,idx))
if numLen % 2 == 1 {
return val1
}
val2 := float64(locateValueByIdx(nums1,nums2,idx - 1))
return (val1 + val2) / 2
}
solution的代码如下:
class Solution {
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length;
int n = B.length;
if (m > n) { // to ensure m<=n
int[] temp = A; A = B; B = temp;
int tmp = m; m = n; n = tmp;
}
int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
while (iMin <= iMax) {
int i = (iMin + iMax) / 2;
int j = halfLen - i;
if (i < iMax && B[j-1] > A[i]){
iMin = i + 1; // i is too small
}
else if (i > iMin && A[i-1] > B[j]) {
iMax = i - 1; // i is too big
}
else { // i is perfect
int maxLeft = 0;
if (i == 0) { maxLeft = B[j-1]; }
else if (j == 0) { maxLeft = A[i-1]; }
else { maxLeft = Math.max(A[i-1], B[j-1]); }
if ( (m + n) % 2 == 1 ) { return maxLeft; }
int minRight = 0;
if (i == m) { minRight = B[j]; }
else if (j == n) { minRight = A[i]; }
else { minRight = Math.min(B[j], A[i]); }
return (maxLeft + minRight) / 2.0;
}
}
return 0.0;
}
}