题目描述
There are two sorted arrays A and B 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)).
难度:分析繁杂,实现时边界容易弄错
分析
首先,这道题可以归类为“已知一个数组,找到其从小到大排在第K位的元素”。
已经限定了时间复杂度是O(log (m+n)),否则的话,用两个下标i和j分别指向这两个数组,如果A[i] < B[j],则i++,否则j++,直到找到第K大的元素为止,时间复杂度O(m+n)。如果到达到题目所说的复杂度,则必须用二分查找了,知易行难啊。
对于这两个数组,设我们的函数是find(A, B, K),
首先,分别取A[K/2-1] 和B[K - (K)/2 -1]比较(记K-K/2为K'),分如下三种情况:
- A[K/2-1] < B[K'-1], 则A[0...(K)/2-1]的所有值都可以忽略了,因为:
- 假设不能忽略,即
A∪B[K-1]
就在A[0...K/2-1]
里面,取最大值A[K/2-1]为A∪B[K-1]
。
由此则B中必定有K'个元素小于A[K/2-1],而又因为A[K/2-1] < B[K'-1],故假设不成立。
- 假设不能忽略,即
- A[K/2-1] > B[K'-1],同理B[0...K'-1]的值均可忽略了。
- A[K/2-1] = B[K'-1],这就是要找的值了,终止。
- 假设发生了情况1(情况2也类似),则我们可以知道:
-
A∪B[K-1]
一定在A(K/2-1...len(A))或B中了,因为步骤1已经去掉了的(K-1)/2个元素(注:它们不是最小元素的,但的确小于A∪B[K-1]
),于是现在应该是寻找下标大小是第K'的元素了。 - 设
A = A[(K/2)..len(A))
,B = B
,K = K - K/2
- 递归到第一步,当其中一个数组长度为0,则另一个取下标K-1即可;另一种情况是,当K为1时,取min(A[0], B[0])
-
最终代码:
public class Solution {
public double findMedianSortedArrays(int A[], int B[]) {
if (A.length == 0 && B.length == 0) return 0;
int len = A.length + B.length;
if (len % 2 == 1) {
return find(A, B, (len + 1) /2);
} else {
double sum = find(A, B, len / 2) +
find(A, B, (len + 2)/2);
return sum / 2;
}
}
double find(int[] A, int[] B, int k) {
if (A.length == 0) return B[k - 1];
if (B.length == 0) return A[k - 1];
if (k == 1) return Math.min(A[0], B[0]);
//永远假设A的长度小于B,这样就可以仅检查A的长度了
//因为对于K,它们之间总有一个的长度绰绰有余
if (A.length > B.length) return find(B, A, k);
int a = Math.min(A.length,k/2);
int b = k - a;
if (A[a-1] < B[b-1]) {
return find(Arrays.copyOfRange(A, a, A.length), B, k - a);
} else if (A[a-1] > B[b-1]) {
return find(A, Arrays.copyOfRange(B, b, B.length), k - b);
} else {
return A[a-1];
}
}
}
如果对空间复杂有要求的话,可以不用Arrays.copyOfRange
的,但是这样就要传入额外的参数指示当前起始点和终止点了,程序会毫不意外的更难看。
吐嘈下,数组下标从0开始真是件反人类的事情。
- 不能通过减下标得到差值,类似
x - 0 = x
迟早被坑出死循环 - 正常思维和下标需要转换,稍有不慎程序就跑挂了
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。