LeetCode 004 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
题目大意:
求两个有序数组中的中位数。
数值二分法解题思路(推荐):
由于是求中位数或第k小的数,根据题型分类用Heap或binary select。Heap的话,由于是有序,所以优势不大,复杂度为O[(m+n)log2].
考虑用binary select,思路比较直接,就是将两个数组看成一个,统计个数的时候由于有序,分别在两数组用二分法统计。
注意事项:
- 中位数有1-2两个。用单一元素数组的例子来确定+1还是-1,Line 6
- 统计最大最小值,注意空数组
- 统计count用bisect不需要+1
Python代码:
1 | def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float: |
算法分析:
时间复杂度为O(nlog(|hi-lo|/delta)),由于数据大小范围是10^6, 而前面的n是基于全数组扫描,这里用二分法,n变成log(m)+log(n)
时间复杂度为O(logm + logn),空间复杂度O(1)
Merge sort算法II解题思路:
既然是有序,考虑用merge sort中的merge步骤
merge sort里面的数组是无序,但这里是有序,所以merge这个步骤可以改进,不是一个个数比较,可以分别取第k/2进行比较,原理是一样的,但效率更高。
若num1[k/2] < num2[k/2]相当于nums1[i] <= nums2[j],此时表示num1[0..k/2]都满足小于等于第k个数的结果数组中。i移位,不是移一位,而是移k/4.
这样得到方法三。方法三解释比较难懂,用mergesort模拟较易懂。
一般是对数组二分,此法采用对k二分
注意事项:
- 若i或j用完,返回另一个数组的元素,下标是j+k或i+k,k此时是剩余的k
Python代码:
1 | def findMedianSortedArrays2(self, nums1: List[int], nums2: List[int]) -> float: |
算法分析:
时间复杂度为O(m + n),空间复杂度O(1)。
二分法算法III解题思路:
将上面的方法换成递归,if判断语句换成递归终止条件,+1变成+k/2,注意判断越界
说起来轻松,但非常容易错,不推荐
注意事项:
- 中位数有1-2两个。
- off by 1的问题。递归子函数要用(k+1)//2而不是k//2因为若k=1时候,k//2就不能移动一位。
- 若nums中i + k // 2越界,不碰nums,右移j,因为nums不能右移那么多, 所以num1赋最大值不是最小值
- 终止条件i >= len(nums1)取大于号,跟上面不同,因为可能越界
Python代码:
1 | def findMedianSortedArrays3(self, nums1: List[int], nums2: List[int]) -> float: |
Java实现
题目要求log(m+n)复杂度,也就提示要对数组总长做二分法。因为要同时处理两个数组所以考虑用递归版二分法。因奇偶中位数有1-2个,
所以加强命题为求两有序数组的第k个数(k>=1)。
对k的值进行二分k/2,先尝试num1数组的第k/2个数和num2数组的第k/2个数。若它们相等,表示它们即为第k个数。也就是比如求第4个数,
分别在两数组中取前两个,平均分配名额。此题类似于求前k大的数(算法文档4.1.13)用统计个数的方法来判断走左还是右。若它们不等,不妨假设
aKey<bKey,这样表示第k个数一定不会是aKey,因为即使比bKey小的数均小于aKey的话(有k/2-1个),总共有k/2-1+k/2-1=k-2个数小于
aKey,也就是aKey为第k-1个数,不可能为第k个数,它只可能出现是bKey或者在num1中比aKey大的数。既然aKey不是解,比aKey小的数
更不可能是解。所以,可以抛掉num1[0, k/2]这部分,转而求num1[k/2+1,nums.length-1]以及nums2的第k-k/2个数,抛掉部分会影响结果
吗?答案是否定的。比如
1 2 3 4
0 6 7 8
k=4,比较2和6,2<6抛掉1 2,转求[3,4],[0,6,7,8]的第2个数,答案仍为3,不影响结果,因为前k/2个数即使有些在nums2也不会影响
最后结果,求的是第k个。
1 2 3 4
0 6 7 8
第k=4数可能出现在num1[k/2+1,nums.length-1]=3或num2=6(下面例子),所以保留部分是正确的。
主要思路是每次递归抛掉k/2个数,数组规模减少k/2,k变成k-k/2, API为left, left2表示新数组的左边界(因为永远抛左边部分)以及k。
递归终止条件为
- left越界,这时此数组的数刚好用完或此数组为空(不会越界太多,否则aKey无穷大会递归到num2)。归结为求另一数组[left..]的第k个数.
- k为1时候,是基本情况,表示求两数组的第1个数,只要返回左端的最小值即可。
注意事项:
- 中位数有1-2两个。
- off by 1的问题。若API中的k定义为第k个数k>=1,若总长len=5,中位数为第len/2+1=3个数。所以在递归函数中,数组下标用到k时
都要-1。抛掉[left, left+k/2-1],递归[left+k/2..]。 - 若nums中left+k/2-1越界,不碰nums,右移left2,因为nums不能右移那么多。
Java代码:
1 | public double findMedianSortedArrays(int[] num1, int[] num2){ |
算法分析:
假设两数组总长度为N,k为中位数即N/2,每次抛掉k/2也就是子问题规模为N-k/2=N-N/4=3N/4. f(N)=f(3N/4)+1.
利用master理论b=4/3, a=1, f(n)=1代入时间复杂度为O(log(m+n)),空间复杂度O(1)。



