问题
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。找出这两个有序数组的中位数。假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.
合并两个有序数组来获得中位数
1、先将两个有序数组合并为一个有序数组
2、再获取中位数
偶数情况中位数索引分别为:(m+n)/2-1,(m+n)/2。将二值求平均数。
计数情况中为数索引为:(m+n)/2
时间复杂度是O(m+n),空间复杂度是O(m+n)。
优化第一个求解方法
上一个算法是将两个有序数组合并为一个有序数组再计算中位数位置。我们可以先计算中位数位置,将两个有序数组元素逐一比较排序过程中,当索引位置到达中位数时停止。
时间复杂度是O((m+n)/2+1)=O(m+n),空间复杂度是((m+n)/2+1)=O(m+n)。
二分查找
一个有序数组的中位数
长度为偶数:
数组a,索引位置初始为0,长度为m。索引 i 将有序数组划分为左右两个长度相等的部分,i = (m - 0) / 2 且必须满足如下两个条件:
1、length(a[0, i)) = length(a[i, m))
2、a[i-1] <= a[i]
则中位数是 (a[i-1] + a[i]) / 2
长度为基数:
数组a,索引位置初始为0,长度为m。索引 i 将有序数组划分为左右两个部分,其中左边部分比右边部分少一个元素,i = (m - 0) / 2 且必须满足如下两个条件:
1、length(a[0, i)) + 1 = length(a[i, m))
2、a[i-1] <= a[i]
则中位数是 a[i]
两个有序数组的中位数
两个有序数组长度之和为偶数:
两个a和b有序数组的长度分别是m和n。对a数组的一个划分i和对b数组的一个划分j,使得a数组的左半部分同b数组的左半部分看作合并后有序数组c的左半部分;而a数组的右半部分同b数组的右半部分看作合并后有序数组c的右半部分。假设有i、j存在,则需满足如下条件:
1、length(a[0, i)) + length(b[0, j]) = length(a[i, m)) + length(b[j, n])
(i-0)+(j-0)=(m-i)+(n-j)
i=(m+n)/2-j
2、max(a[i-1], b[j-1]) <= min(a[i], b[j])
则中位数是 (max(a[i-1], b[j-1]) + min(a[i], b[j])) / 2
两个有序数组长度之和为基数:
两个a和b有序数组的长度分别是m和n。对a数组的一个划分i和对b数组的一个划分j,使得a数组的左半部分同b数组的左半部分看作合并后有序数组c的左半部分;而a数组的右半部分同b数组的右半部分看作合并后有序数组c的右半部分。假设有i、j存在,则需满足如下条件:
1、length(a[0, i)) + length(b[0, j]) + 1 = length(a[i, m)) + length(b[j, n])
(i-0)+(j-0)+1=(m-i)+(n-j)
i=(m+n-1)/2-j
2、max(a[i-1], b[j-1]) <= min(a[i], b[j])
则中位数是 min(a[i], b[j])
分析求解:
两个有序数组长度不管是基数还是偶数,都需要满足如上两个条件。其中第二个条件相同,而第一个条件我们可以归纳简化为i=(m+n)/2-j,因为当m+n为基数时 (m+n)/2-j = (m+n-1)/2-j。所以,只要找出数组a划分i,则可以通过公式i=(m+n)/2-j,得到数组b划分j,并且满足如下两个条件:
1、i=(m+n)/2-j
2、max(a[i-1], b[j-1]) <= min(a[i], b[j])
我们可以在数组a中,通过折半查找来取得划分i。其中IMIN为开始索引0,IMAX为数组a长度m
1)将数组a折半 i = (IMAX - IMIN) / 2
2)其中a[i-1]<=a[i],b[j-1]<=b[j]一定是成立的。
若b[j-1]>a[i]不符合条件,i只有向后移动一位变大,相应的j向前移动一位变小,才有可能符合条件b[j-1]<=a[i]。所以,此时的i并不是有效划分,有效划分在i+1~IMAX范围内。IMIN赋值为i+1,继续执行1)。
若a[i-1]>b[j]不符合条件,i只有向前移动一位变小,相应的j向后移动一位变大,才有可能符合条件a[i-1]<=b[j]。所以,此时的i并不是有效划分,有效划分在IMIN~i-1范围内。IMAX为i-1,继续执行1)。
3)最后找到合适的i和j,就可以直接计算得到中位数。
注意:
1、数组a和数组b长度的关系
必须保证0<=i<=m, 0<=j<=n;假设0<=i<=m成立,如果满足0<=j<=n,m和n之间的关系。
1)
i=(m+n)/2-j =>
j=(m+n)/2-i =>
因为i<=m,所以j=(m+n)/2-i>=(m+n)/2-m=(n-m)/2 =>
若(n-m)/2>=0,则j>=0 =>
m<=n
2)
i=(m+n)/2-j =>
j=(m+n)/2-i =>
因为i>=0,j=(m+n)/2-i<=(m+n)/2 =>
若(m+n)/2<=n,则j<=n =>
m<=n
3)
总结:当m<=n时,0<=i<=m,可以保证0<=j<=n。所以,在算法中,始终要保持数组a的长度要不大于数组b的长度。
数组a可能为空数组
2、i,j移动过程中的越界问题
1)若b[j-1]>a[i]不符合条件,i只有向后移动一位变大,相应的j向前移动一位变小,才有可能符合条件b[j-1]<=a[i]。
为保证数组不越界,j不能等于0,i不能等于m。
这两种情况要单独判断:
当j==0时,左半部分的最大值是a[i-1],右半部分的最小值是min(a[i], b[0]);
当i==m时,左半部分的最大值是max(a[m-1], b[j-1]),右半部分的最小值是b[j]。
2)若a[i-1]>b[j]不符合条件,i只有向前移动一位变小,相应的j向后移动一位变大,才有可能符合条件a[i-1]<=b[j]。所以,此时的i并不是有效划分,有效划分在0~i范围内。IMIN赋值为0,IMAX为i,继续执行1)。
为保证数组不越界,i不能等于0,j不能等于n。
这两种情况要单独判断:
当i==0时,左半部分的最大值是b[j-1],右半部分的最小值是min(a[0], b[j]);
当j==n时,左半部分的最大值是max(a[i-1], b[n-1]),右半部分的最小值是a[i]。
时间复杂度是O(log(min(m,n))),空间复杂度是O(1)。
性能分析
随机生成两个有序数组进行性能测试:
The Median is 512203030.5 by MergedSortedArrays solve(num1.length=42385384,num2.length=19210176), using time is 349 milliseconds.
The Median is 512203030.5 by OptimizingMergedSortedArrays solve(num1.length=42385384,num2.length=19210176), using time is 195 milliseconds.
The Median is 512203030.5 by Binary solve(num1.length=42385384,num2.length=19210176), using time is 0 milliseconds.
二分查找法性能最优,符合预期。
如果有什么问题可以私信我,还有一些关于java资料都可以私信问我要。
喜欢的大家点赞加关注并转发一下.
还有一些互联网公司java程序员面试涉及到的绝大部分面试题和答案做成了文档和架构视频资料还有完整高清的java进阶架构学习思维导图免费分享给大家(包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术资料),希望能帮助到您面试前的复习且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
来源:CSDN
作者:程序员plus
链接:https://blog.csdn.net/AD_plus/article/details/98967497