[LeetCode] 求两个有序数组的中位数
2017-06-09 10:33
274 查看
[LeetCode] 求两个有序数组的中位数
From: https://leetcode.com/problems/median-of-two-sorted-arrays/#/solutions我的个人博客:http://riot-qiu.coding.me/2017/06/08/median-of-two-sorted-arrays
问题描述
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
Tips
Binary Search、Array、 Divide and Conquer解法
为了解决这个问题,我们首先要明白中位数的作用。在统计学中,中位数能够将一组数据划分为两个长度相同的数据集,其中一个子集总是大于另外一个子集。 如果我们能够理解在划分上的作用,那么我们离正确答案已经非常接近了。划分AB
首先我们用一个随机位置i将A划分成两个部分:left_A | right_A A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
假设A有m个元素,那么
i = 0 ~ m。然后我们可以得到以下信息:
left_A.length = i, right_A.length = m - i;
PS:当
i==0, left_A 是空的,但
i==m, right_A 是空的。
同样我们用一个随机位置j将B划分成两部分:
left_B | right_B B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
合并AB左右子集
把left_A和
left_B放在同一集合,把
right_A和
right_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]
如果我们能确定下面两个条件:
1) len(left_part) == len(right_part) 2) max(left_part) <= min(right_part)
那么我们就已经把{A,B}这个集合分成了两个相同长度的部分,而且右边始终大于左边。那么中位数就等于
median = (max(left_part) + min(right_part))/2.
PS:左边最大和右边最小之和的一半
条件限定
为了保证左右数量两边相等和左边最大小于等于右边最小,我们就需要保证:(1) i + j == m - i + n - j (or: m - i + n - j + 1) if n >= m, 我们只需要确定i和j的关系: i = 0 ~ m, j = (m + n + 1)/2 - i (2) B[j-1] <= A[i] and A[i-1] <= B[j]
说明:
1. 为了过程简单,我们先假设
A[i-1],B[j-1],A[i],B[j]这几个是一直有效的。(后面会说明怎么处理这些边界情况)
2. 为什么要
n>=m呢?因为这是为了保证在
i在大于等于0小于等于m的取值中,一直保持有效。因为
j=(m+n+1)/2-i,如果
i=m且
n<m,那么
j<0,这会得到错误的答案。
i在[0,m]中取值,找到满足
B[j-1] <= A[i] and A[i-1] <= B[j]的情况,这个时候的i就是中间的位置。
算法过程
设置imin = 0, imax = 0, 然后在
[imin, imax]这个范围里面找
设置i和j:
i = (imin + imax)/2, j = (m + n + 1)/2 - i
这样我们就保证了左边部分和右边部分已经长度相同。下面我们有三种情况需要考虑:
如果i和j满足
B[j-1] <= A[i] and A[i-1] <= B[j],意味当前ij已经就是我们要找的位置,停止查找。
如果
B[j-1] > A[i],那么意味A[i]太小了,我们需要调整i让条件
B[j-1] <= A[i]满足。
我们可以增大i吗?
可以,因为当i增大,j势必会减小,那么对应B[j-1]会减少,A[i]会增大,经过调整
B[j-1] <= A[i]可能刚好能够满足。
我们可以减少i吗?
不可以,与之前所述的相反,调整后条件
B[j-1] <= A[i]依旧无法满足。
所以我们要增大i。就是调整当前的搜索范围到
[i+1, imax],即设置
imin = i+1,然后回到第二步。
如果
A[i-1] > B[j],那么意味
A[i-1]过大,与前一个条件相反,我们需要减少i来满足条件
A[i-1]<=B[j],就是调整当前的搜索范围到
[imin, i-1],即设置
imax = i-1,然后回到第二步处理。
处理边界值
现在我们来考虑边界问题,当i和j等于i=0,i=m,j=0,j=n这四种情况的时候,
A[i-1],B[j-1],A[i],B[j]这四个位置的值也有可能不存在。实际上,这个几种情况的处理比你想象中的要简单。
正常情况下,只要i和j不是边界,
A[i-1],B[j-1],A[i],B[j]这四个值必定存在,我们只需要确定条件
B[j-1] <= A[i]和条件
A[i-1] <= B[j]。当存在
A[i-1],B[j-1],A[i],B[j]没有值的情况,我们可能不需要确定两个条件。比如:当
i=0,
A[i-1]不存在,那么我们就不需要确定条件
A[i-1] <= B[j].
所以判断的条件就变成:
B[j-1] <= A[i] && A[i-1] <= B[j] ===> (j == 0 || i == m || B[j-1] <= A[i]) && (i == 0 || j == n || A[i-1] <= B[j])
而之前的循环需要判断的三个条件就变成:
1. 如果满足
(j == 0 || i == m || B[j-1] <= A[i]) && (i == 0 || j == n || A[i-1] <= B[j])那么当前i就是要找的位置。
2. 如果
j > 0 and i < m and B[j - 1] > A[i],那么当前i太小。
3. 如果
i > 0 and j < n and A[i - 1] > B[j], 那么当前的i太大。
因为m<=n,根据i的取值范围定义,我们可以得出下面结论,所以<2>、<3>中的
j>0, j<n可以忽略。
m <= n, i < m ==> j = (m+n+1)/2 - i > (m+n+1)/2 - m >= (2*m+1)/2 - m >= 0 m <= n, i > 0 ==> j = (m+n+1)/2 - i < (m+n+1)/2 <= (2*n+1)/2 <= n i < m ==> j > 0; i > 0 ==> j < n;
具体代码实现
public double findMedianSortedArrays(int[] nums1, int[] nums2) { int m = nums1.length; int n = nums2.length; if (m > n) { m = n; n = nums1.length; int[] tmp = nums1; nums1 = nums2; nums2 = tmp; } int imin =0, imax=m,i,j,halfLen=(m+n+1)/2; while (imin <= imax) { i = (imin+imax)/2; j = halfLen - i; if (i < m && nums2[j-1] > nums1[i]) { imin = i + 1; } else if(i > 0 && nums1[i-1] > nums2[j]) { imax = i - 1; } else { int left_max; if (j==0) { left_max = nums1[i-1]; } else if (i ==0 ) { left_max = nums2[j-1]; } else { left_max = Math.max(nums1[i-1],nums2[j-1]); } if ((m+n)%2 == 1) { return left_max; } int right_min; if (i == m) { right_min = nums2[j]; } else if ( j == n) { 979d right_min = nums1[i]; } else { right_min = Math.min(nums1[i],nums2[j]); } return (left_max+right_min)/2.0; } } return -1; }
相关文章推荐
- 【Leetcode】两个有序数组的中位数
- leetcode之数组类之数组的旋转与分治类-----OJ 189/33/81/153/154 数组旋转 旋转数组搜索 88 有序数组合并 4 两个有序数组寻找第K个元素/中位数 35 寻找插入位置
- Median of Two Sorted Arrays 两个有序数组的中位数@LeetCode
- [LeetCode] Median of Two Sorted Arrays 两个有序数组的中位数
- [LeetCode] Median of Two Sorted Arrays 两个有序数组的中位数
- [LeetCode] 4. Median of Two Sorted Arrays 两个有序数组的中位数
- [LeetCode]Median of Two Sorted Arrays 二分查找两个有序数组的第k数(中位数)
- LeetCode 4. Median of Two Sorted Arrays(两个有序数组的中位数)
- 【LeetCode】4. Median of Two Sorted Arrays两个有序数组的中位数
- [LeetCode] Median of Two Sorted Arrays 两个有序数组的中位数
- 【LeetCode 4. Median of Two Sorted Arrays】两个有序数组的中位数求解
- LeetCode 4. Median of Two Sorted Arrays(两个有序数组的中位数)
- [LeetCode]4 两个有序数组的中位数
- Median of Two Sorted Arrays 两个有序数组的中位数@LeetCode
- [leetcode] Median of Two Sorted Arrays 寻找两个有序数组的中位数
- 【算法之美】求解两个有序数组的中位数 — leetcode 4. Median of Two Sorted Arrays
- [LeetCode]Median of Two Sorted Arrays 二分查找两个有序数组的第k数(中位数)
- LeetCode 4. Median of Two Sorted Arrays 求两个有序数组的中位数
- 求两个有序数组的中位数(无论数组长度是否一样)
- 给定两个有序的n长度的数组,如何找出这两个数组合并后的中位数?