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. 基于深度学习的中文语音识别系统框架(pluse)

    目录 声学模型 GRU-CTC DFCNN DFSMN 语言模型 n-gram CBHG 数据集 本文搭建一个完整的中文语音识别系统,包括声学模型和语言模型,能够将输入的音频信号识别为汉字. 声学模型 ...

  2. nordic mesh 任务调度实现

    nordic mesh 任务调度实现 nordic mesh的任务调度室基于定时器实现的,有两个链表结构维护任务. 需要注意的是,任务调度的部分接口只能在"bearer event" ...

  3. Java学习个人备忘录之数组

    数组 概念:同一种类型数据的集合,其实数组就是一个容器. 数组的好处:可以自动给数组中的元素从0开始编号,方便操作这些元素. 格式1:元素类型[] 数组名 = new 元素类型[元素个数]; 格式2: ...

  4. JavaScript初探系列之String的基本操作

    1.字符串转换 字符串转换是最基础的要求和工作,你可以将任何类型的数据都转换为字符串,你可以用下面三种方法的任何一种: var myStr = num.toString(); // "19& ...

  5. iOS-开发将文本复制到剪切板

    下面方法可以将文本复制到剪切板 UIPasteboard *pboard = [UIPasteboard generalPasteboard]; pboard.string = @"邀请码& ...

  6. 第二章 持续集成jenkins工具使用之系统基本设置

    Jenkin系统初始化成功后,会进入用户设置页面,设置用户信息后即可进入系统,如果没有设置用户,jenkins系统默认的用户是admin,密码administrator 1.1         Con ...

  7. array_intersect_assoc 与array_intersect区别

    1.array_intersect_assoc — 带索引检查计算数组的交集 说明 array array_intersect_assoc ( array $array1 , array $array ...

  8. java中sql语句能不能加分号的问题?

    一.原因  在程序运行中,当执行sql后总是报无效字符错误:但是把程序放在pl/sql中执行又没有错误.让我很纳闷!于是我开始查找资料,然后我终于发现了问题. 二.问题剖析 原来在程序中:如果你在程序 ...

  9. 在select中,载入时默认select为空白,选项内不显示空白项

    有两种办法,一种纯css实现,一种借助js实现. html: <body onload="load()"> <select id="abc" ...

  10. HTML5拖拽表格中单元格间的数据库

    效果图: