1、Median of Two Sorted Arrays——这是leedcode的第四题:
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)).
题目意思很简单,两个有序序列,找出这两个有序序列合并后的中位数。时间复杂度要求O(log (m+n));
解决思路:
1、习惯性思路(错误的):
利用排序将两个数组合并成一个数组,然后返回中位数;如果使用这种算法, 时间复杂度是O(m+n),并不能满足要求。
2、正确思路:
这个思路不是我自己想出来,是从讨论区里看到的。这里总结如下:
a、这里将两个序列命名为A、B;
b、将两个序列分别分为左右两个部分分别命名left_part 和right_part:
left_part | right_part
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
c、如果我们可以确保以下两个条件,我们就可以将两个序列分成如上两个部分;
1) len(left_part) == len(right_part) 左边的长度等于右边的长度
2) max(left_part) <= min(right_part)左边最大的数,小于等于右边最小的数。
只要
(1) i + j == m - i + n - j (或者: m - i + n - j + 1)
如果n >= m,化简上面式子,可以得到:当 i = 0 ~ m, j = (m + n + 1)/2 - i
(2) B[j-1] <= A[i] and A[i-1] <= B[j]
d、得到上面的公式之后,其实就可以很清晰的找到那个i,从而得到中位数。但是时间复杂度没达到要求。考虑到两个序列分别有序,所以我们可以使用二分查找,这样时间复杂度就可以达到了。
<1> 令 imin = 0, imax = m, 然后在[imin, imax]中查找i
<2> 令i = (imin + imax)/2, j = (m + n + 1)/2 - i
<3> 此时可以满足len(left_part)==len(right_part).在查找的过程中,会出现如下三种情况:
<a> B[j-1] <= A[i] and A[i-1] <= B[j]
如果这个情况,意味着我们找到了那个i,停止查找;
<b> B[j-1] > A[i]
意味着A[i]太小了,需要调整i,从而得到B[j-1] <= A[i];这里需要增加i,因为j和i是反相关,i增加j就减小。所以我们需要调整i的变动范围从[i+1, imax].所以设置imin = i+1,跳转到<2>继续执行.
<c> A[i-1] > B[j]
意味着A[i-1]太大了,同理,需要减小i的值,从而使A[i-1]<=B[j]满足,所以调整查找范围为[imin, i-1]。设置imax = i-1, 跳转到<2>继续执行。
e、这时候,我们需要解决的,就是边界条件问题,当i=0/i=m/j=0/j=n时,A[i-1],B[j-1],A[i],B[j]这些值都是边界值。而我们可以很简单的通过判断就知道中位数在哪里。至此,思路已经很清晰了。
这里贴出Accept的代码:
Java版本:
public class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int size1 = nums1.length, size2 = nums2.length;
int max, min;
int i, j;
int max_of_left, min_of_right;
int halfLength = (size1 + size2 + 1) / 2;
// 如果nums长度大于nums2,就交换
if (size1 > size2) {
int temp = size1;
size1 = size2;
size2 = temp;
int[] tempArray = nums1;
nums1 = nums2;
nums2 = tempArray;
}
min = 0;
max = size1;
// 以下是二分查找
while (min <= max) {
i = (min + max) / 2;
j = halfLength - i;
if (i < size1 && j > 0 && nums1[i] < nums2[j - 1]) {// 前两个条件是确保不会走到边界值,当nums1[i]<nums2[j-1]时,增大查找下限
min = i + 1;
} else if (j < size2 && i > 0 && nums2[j] < nums1[i - 1]) {// 前两个条件是确保不会走到边界值,当nums2[j]<nums1[i-1]时,减小查找上限
max = i - 1;
} else {
// 找到这个i,满足条件,可以得到中位数
// *************************//
// 以下是边界值的判断
if (i == 0) {
max_of_left = nums2[j - 1];
} else if (j == 0) {
max_of_left = nums1[i - 1];
} else {
// 否则,二者最大的数为左边最大的数
max_of_left = nums1[i - 1] > nums2[j - 1] ? nums1[i - 1] : nums2[j - 1];
}
// 如果两者之和,是奇数,因为要确保两边的长度一样,也就是
// i + j == m - i + n - j 或i + j == m - i + n - j + 1
// 这里设置halfLength = (size1 + size2 + 1) 2;
// 所以如果两者和为奇数,则左边比右边多一个数,中位数在左边,且是最大的。
if ((size1 + size2) % 2 == 1) {
return max_of_left;
}
if (i == size1) {
min_of_right = nums2[j];
} else if (j == size2) {
min_of_right = nums1[i];
} else {
min_of_right = nums1[i] < nums2[j] ? nums1[i] : nums2[j];
}
return ((float) (max_of_left + min_of_right)) / 2;
}
}
return 0;
}
}
C++版本:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
if (m>n)
{
return findMedianSortedArrays(nums2, nums1);
}
int min = 0, i;
int max = m, j;
int halfLength = (m + n + 1) >> 1;
while (min <= max) {
i = (min + max) >> 1;
j = halfLength - i;
if (j > 0 && i < m&&nums1[i] < nums2[j - 1]) {
min = i + 1;
}
else if (i > 0 && j < n&&nums2[j] < nums1[i - 1])
{
max = i - 1;
}
else
{
int maxOfLeft;
if (i==0)
{
maxOfLeft = nums2[j - 1];
}
else if(j==0)
{
maxOfLeft = nums1[i - 1];
}
else
{
maxOfLeft = nums1[i - 1] > nums2[j - 1] ? nums1[i - 1] : nums2[j - 1];
}
if ((m+n)%2==1)
{
return maxOfLeft;
}
int minOfRight;
if (i==m)
{
minOfRight = nums2[j];
}
else if (j==n)
{
minOfRight = nums1[i];
}
else
{
minOfRight = nums1[i] < nums2[j] ? nums1[i] : nums2[j];
}
return ((float)minOfRight + maxOfLeft) / 2;
}
}
return 0;
}
};