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)).

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

解法1: 将问题转化为经典的“求两个有序数组中的第k小值”问题,即:

  首先假设有序数组A和B的长度都大于k/2(下取整),比较A[k/2-1]和B[k/2-1]两个元素,这两个元素分别表示A和B中的第k/2小的元素。这两个元素比较共有三种情况:>、<和=。

  (1)如果A[k/2-1]<B[k/2-1],说明A[k/2-1]及其之前的元素肯定都在A和B所有元素的前(k-1)小元素中,也就是说,A[k/2-1]不可能大于两个数组所有元素的第k小值。因此,将A的前(k/2)个元素删去之后(设剩余部分为A'),进一步求A'和B中的第(k-k/2)大的数。

  (2)如果A[k/2-1]<B[k/2-1],同理。

  (3)如果A[k/2-1]=B[k/2-1],如果k为偶数,则A[k/2-1]和B[k/2-1]即为第k小数,因为两个数组中分别有(k/2-1)个元素小于该值。但考虑到k可能不为偶数,可将其中一个的前(k/2)个元素删去之后,求剩余部分的第(k-k/2)大的数。

  需要注意的是,如果A的长度m<k/2,应取A[m-1]与B[k/2-1](或B[k-m-1])比较;B的长度小于k/2时同理。

  通过上面的分析,我们即可以采用递归的方式实现寻找第k小的数。此外我们还需要考虑几个边界条件:

    • 如果A或者B为空,则直接返回B[k-1]或者A[k-1];
    • 如果k为1,我们只需要返回A[0]和B[0]中的较小值。
public class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int len1 = nums1.length;
int len2 = nums2.length;
int left = (len1 + len2 + 1) / 2;
int right = (len1 + len2 + 2) / 2;
return (findKthSmallest(nums1, nums2, left) + findKthSmallest(nums1, nums2, right)) / 2.0;
} // 将问题转化为求两个数组的第k小数
public int findKthSmallest(int[] nums1, int[] nums2, int k) {
int len1 = nums1.length;
int len2 = nums2.length;
int half = k / 2; // 如果有其中一个长度为零,直接返回另一个的第k小数
if (len1 == 0) return nums2[k - 1];
if (len2 == 0) return nums1[k - 1];
// 如果k为1,则直接返回两个数组的最小数
if (k == 1) return Math.min(nums1[0], nums2[0]); // 判断half是否超过了数组长度
int cutPoint1 = Math.min(len1, half);
int cutPoint2 = Math.min(len2, half); // 判断两个数组切割点处的值大小,将小的数组从切割点处截去
if (nums1[cutPoint1 - 1] < nums2[cutPoint2 - 1]) {
return findKthSmallest(Arrays.copyOfRange(nums1, cutPoint1, len1), nums2, k - cutPoint1);
} else {
return findKthSmallest(nums1, Arrays.copyOfRange(nums2, cutPoint2, len2), k - cutPoint2);
}
}
}

解法2:

-------------------- 准备工作 --------------------

  对于长为N的数组A来说,用L和R分别表示中位数切割点的值(N为奇数)或者左右两侧的值(N为偶数),则L=(N-1)/2, R=N/2, 所以中位数可以表示为: (L+R) / 2 = (A[(N-1)/2] + A[N/2]) / 2。

  在数组的每两个数字之间添加“虚拟位置”(用#表示),同时把数字也当成“位置”,如:

[6 9 13 18]  ->   [# 6 # 9 # 13 # 18 #]    (N = 4)
position index 0 1 2 3 4 5 6 7 8 (N_Position = 9) [6 9 11 13 18]-> [# 6 # 9 # 11 # 13 # 18 #] (N = 5)
position index 0 1 2 3 4 5 6 7 8 9 10 (N_Position = 11)

  可以看出,对于长度为N的数组,总共有(2*N+1)个位置,无论N为奇数还是偶数,而中位数切割点的位置也总是第N个(下标从0开始)。

-------------------- 算法原理 --------------------

  设有序数组A1和A2,A1的长度>A2的长度:

A1: [# 1 # 2 # 3 # 4 # 5 #]    (N1 = 5, N1_positions = 11)
pos: 0 1 2 3 4 5 6 7 8 9 10 A2: [# 1 # 1 # 1 # 1 #] (N2 = 4, N2_positions = 9)
pos: 0 1 2 3 4 5 6 7 8

  与一个数组的中位数问题一样,我们需要对两个数组进行切割,使得左侧的所有数字 < 右侧的所有数字。

  可以注意到:

  1. 总共有(2N1 + 2N2 + 2)个位置。因此切割点的左右两侧应该分别有(N1 + N2)个位置,切割点本身占了两个位置。
  2. 因此,假设A2的切割点 C2 = k,那么A1的切割点位置必为 C1 = N1 + N2 -k。例如:如果 C2 = 2,则 C1 = 4 + 5 - C2 = 7。
     [# 1 # 2 # 3 # (4/4) # 5 #]    
    
     [# 1 / 1 # 1 # 1 #]  
  3. 切割之后,A1切割为L1+R1,A2切割为L2+R2,即
     L1 = A1[(C1-1)/2]; R1 = A1[C1/2];
    L2 = A2[(C2-1)/2]; R2 = A2[C2/2];

    在上述例子中,

        L1 = A1[(7-1)/2] = A1[3] = 4; R1 = A1[7/2] = A1[3] = 4;
    L2 = A2[(2-1)/2] = A2[0] = 1; R2 = A1[2/2] = A1[1] = 1;

  现在如何确定切割点是不是我们想要的?由于L1和L2是左侧的两个最大值,而R1和R2是右侧的两个最小值,我们只需满足:

L1 <= R1  &&  L1 <= R2  &&  L2 <= R1  &&  L2 <= R2

从而保证左侧的数字 <= 右侧的数字。由于A1和A2是有序的,即满足L1 <= R1 和 L2 <= R2,因此只需满足

L1 <= R2 和 L2 <= R1即可。

  现在我们可以应用二分法进行查找:

  • 如果 L1 > R2,说明A1左侧有较多大数,因此必须将C1左移(C2右移);
  • 如果 L2 > R1,说明A2左侧有较多大数,因此必须将C2左移(C1右移);
  • 除了以上两种情况,得到的切割点即为正确的,此时可以计算出中位数为 (max(L1, L2) + min(R1, R2)) / 2。

  需要注意的是:

  • 由于C1和C2可以由彼此推出,一般选择较短的A2采用二分法确定C2的位置之后,再计算C1的位置,因此时间复杂度为O(log(min(N1, N2)))。
  • 边界情况:边界情况出现在当切割点位于 0th 或者 2Nth 的时候。例如,当 C2 = 2N2 时,R2 = A2[2 * N2 / 2] = A2[N2] 将超出数组下标范围。为解决此问题,假设在数组两侧有两个额外的数,即 A[-1] = Integer.MIN_VALUE, A[N] = Integer.MAX_VALUE。
public class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
if (m < n) return findMedianSortedArrays(nums2, nums1);
if (n == 0) return (nums1[(m - 1) / 2] + nums1[m / 2]) / 2.0; int left = 0;
int right = 2 * n;
while (left <= right) {
int mid2 = (left + right) / 2;
int mid1 = m + n - mid2;
int L1 = mid1 == 0 ? Integer.MIN_VALUE : nums1[(mid1 - 1) / 2];
int R1 = mid1 == m * 2 ? Integer.MAX_VALUE : nums1[mid1 / 2];
int L2 = mid2 == 0 ? Integer.MIN_VALUE : nums2[(mid2 - 1) / 2];
int R2 = mid2 == n * 2 ? Integer.MAX_VALUE : nums2[mid2 / 2]; if (L1 > R2)
left = mid2 + 1;
else if (L2 > R1)
right = mid2 - 1;
else
return (Math.max(L1, L2) + Math.min(R1, R2)) / 2.0;
}
return -1;
}
}

ps: 如果没有时间复杂度为 O(og(m+n)) 的限制,也可以定义两个指针,分别从两个数组的头部开始,比较指向元素的大小,较小的指针往后移,然后再次比较。。。。若 (m+n) 为奇数,则移动 (m+n-1)/2 次后,指针指向的数为中位数;若(m+n) 为偶数,则移动 (m+n)/2-1 和 (m+n)/2 次后,指针分别指向的两个数的平均值为中位数。

参考资料:

https://discuss.leetcode.com/topic/16797/very-concise-o-log-min-m-n-iterative-solution-with-detailed-explanation

[LeetCode] 4. Median of Two Sorted Arrays ☆☆☆☆☆的更多相关文章

  1. 【算法之美】求解两个有序数组的中位数 — leetcode 4. Median of Two Sorted Arrays

    一道非常经典的题目,Median of Two Sorted Arrays.(PS:leetcode 我已经做了 190 道,欢迎围观全部题解 https://github.com/hanzichi/ ...

  2. LeetCode(3) || Median of Two Sorted Arrays

    LeetCode(3) || Median of Two Sorted Arrays 题记 之前做了3题,感觉难度一般,没想到突然来了这道比较难的,星期六花了一天的时间才做完,可见以前基础太差了. 题 ...

  3. LeetCode 4 Median of Two Sorted Arrays (两个数组的mid值)

    题目来源:https://leetcode.com/problems/median-of-two-sorted-arrays/ There are two sorted arrays nums1 an ...

  4. Leetcode 4. Median of Two Sorted Arrays(二分)

    4. Median of Two Sorted Arrays 题目链接:https://leetcode.com/problems/median-of-two-sorted-arrays/ Descr ...

  5. LeetCode 4. Median of Two Sorted Arrays & 归并排序

    Median of Two Sorted Arrays 搜索时间复杂度的时候,看到归并排序比较适合这个题目.中位数直接取即可,所以重点是排序. 再来看看治阶段,我们需要将两个已经有序的子序列合并成一个 ...

  6. 第三周 Leetcode 4. Median of Two Sorted Arrays (HARD)

    4. Median of Two Sorted Arrays 给定两个有序的整数序列.求中位数,要求复杂度为对数级别. 通常的思路,我们二分搜索中位数,对某个序列里的某个数 我们可以在对数时间内通过二 ...

  7. Leetcode 4. Median of Two Sorted Arrays(中位数+二分答案+递归)

    4. Median of Two Sorted Arrays Hard There are two sorted arrays nums1 and nums2 of size m and n resp ...

  8. LeetCode 004 Median of Two Sorted Arrays

    题目描述:Median of Two Sorted Arrays There are two sorted arrays A and B of size m and n respectively. F ...

  9. leetcode 4. Median of Two Sorted Arrays

    https://leetcode.com/problems/median-of-two-sorted-arrays/ There are two sorted arrays nums1 and num ...

  10. leetcode之 median of two sorted arrays

    这是我做的第二个leetcode题目,一开始以为和第一个一样很简单,但是做的过程中才发现这个题目非常难,给人一种“刚上战场就踩上地雷挂掉了”的感觉.后来搜了一下leetcode的难度分布表(leetc ...

随机推荐

  1. Python3 标准库:sys

    import sys print(sys.argv[0]) print(sys.argv[1]) print(len(sys.argv)) print(str(sys.argv)) print(sys ...

  2. ServiceStack.Ormlit 事务

    应该使用这个方法开启事务 public static IDbTransaction OpenTransaction(this IDbConnection dbConn) { return new Or ...

  3. ffmpeg接收rtsp流问题

    项目使用mingw环境g++5.3,C++调用ffmpeg接收rtsp流,再通过C#显示.结构上是C#调用C++的so文件,读取得到的视频帧(RGB888格式),通过图片控件显示. 一开始是使用ope ...

  4. c# 计算两个时间的时间差

    //计算2个日期之间的天数差 DateTime dt1 = Convert.ToDateTime("2007-8-1"); DateTime dt2 = Convert.ToDat ...

  5. Gym - 100851F Froggy Ford kruskal

    题目链接: http://acm.hust.edu.cn/vjudge/problem/307216 Froggy Ford Time Limit: 3000MS 题意 青蛙过河,河中有若干个石头,现 ...

  6. sublime Remote_encoding cp1252

    "remote_encoding": "cp1252",才能连接远程ftp

  7. linux解压zip

    用 unzip 的先安装 yum install -y unzip #unzip file.zip -d /root  -d指解压路径 ,不写的话默认当前目录

  8. 异步请求Python库 grequests的应用和与requests库的响应速度的比较

    requests库是python一个优秀的HTTP库,使用它可以非常简单地执行HTTP的各种操作,例如GET.POST等.不过,这个库所执行的网络请求都是同步了,即cpu发出请求指令后,IO执行发送和 ...

  9. MySQL中使用trim()删除两侧字符

    在某些情况下由于程序没处理好,导致数据表中有些字段的值会有空白字符出现,如 这样如果在严格比对name时会匹配不到.trim()函数可以解决这样的问题 作为trim()函数的子集,ltrim()函数是 ...

  10. mysql学习之数据备份与恢复

    该文使用mysql5.5 centos6.5 64位(本人使用rpm安装mysql,数据库的安装目录默认) 一.数据备份注意事项 读锁问题:数据库(或者某个表)一旦进行读锁操作则影响数据库的写操作所以 ...