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

    PK3Err0040 The target device is not ready for debugging. Please check your configuration bit setting ...

  2. Java 抽象类和Final关键字

    抽象类 用abstract关键字来修饰一个类时,这个类叫抽象类: 用abstract关键字来修饰一个方法时,该方法叫做抽象方法. 含有抽象方法的类必须被定义而为抽象类,抽象类必须被继承,抽象方法必须被 ...

  3. lintcode-11-二叉查找树中搜索区间

    二叉查找树中搜索区间 给定两个值 k1 和 k2(k1 < k2)和一个二叉查找树的根节点.找到树中所有值在 k1 到 k2 范围内的节点.即打印所有x (k1 <= x <= k2 ...

  4. LintCode-50.数组剔除元素后的乘积

    数组剔除元素后的乘积 给定一个整数数组A. 定义B[i] = A[0] * ... * A[i-1] * A[i+1] * ... * A[n-1], 计算B的时候请不要使用除法. 样例 给出A=[1 ...

  5. Linux防火墙iptables学习

    http://blog.chinaunix.net/uid-9950859-id-98277.html 要在网上传输的数据会被分成许多小的数据包,我们一旦接通了网络,会有很多数据包进入,离开,或者经过 ...

  6. IPReversePathFilter

    nstat TcpExtIPReversePathFilter for i in /proc/sys/net/ipv4/conf/*/rp_filter ; do > echo 0 > $ ...

  7. 使用协程(gevent)实现请求

    协程,又称微线程.英文名Coroutine. 协程最大的优势就是协程极高的执行效率.因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就 ...

  8. [转]Windows 7 蓝屏后获取 MEMORY.DMP 文件及注意事项

    转自:http://hi.baidu.com/guicomeon/item/d6753a177fc76f0f8fbde46a 系统默认会在 C:\Windows 目录下创建 MEMORY.DMP 文件 ...

  9. 修改MSSQL字段类型

    update Data_Project set SyncTime=NULL; alter table Data_Project alter column SyncTime datetime; upda ...

  10. VBA 实现学校上课教员一学期中所有上课时间,在一页中通过背景底色反应出来

    需求:学校一学期的所有课程表,每个教员都有可能上好几门课,但给一个教员调课时需要查找所调课时间位置有没有此教员上其它的课 相冲突,手动查找很不方便,这里想通过一个表中位置显示出同一教员在所有课表中出现 ...