4. 寻找两个有序数组的中位数
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
中位数就是把一个升序数组分成了长度相等的两部分,其中左半部分的最大值永远小于等于右半部分的最小值,这道题就是把nums1和nums2合并成一个大数组,num1和num2里面的数可能在中位数的左右,而又因为这两个数组都是升序的,所以这两个数组中会分别存在一个临界值i和j把数组分成了两部分,在这个值i,j左边的数都在大数组中位数的左边,在这个值i,j的右边的数都在大数组中位数的右边。并且为了让大数组左右两边长度相等,则i和j要符合两个条件,i+j = (m+n+1)/2 之所以m+n后面还要加1是为了应对大数组长度为奇数的情况。 Max(nums1[i-1],nums2[j-1])<=Min(num1[i],nums2[j]) 这个是因为在临界值左边的最大的那个数永远要小于等于在临界值位置的最小的那个值。
由于m+n的值是恒定的,所以我们只需要确定了i的值,就能相应的得到j的值。大体流程如下:
1.利用二分查找确定i,然后根据i的值得到j.
2.验证i和j的值是不是两个数组的临界值,这其中分为3种情况 a: nums1[i-1]<=nums2[j] && nums2[j-1]<=nums1[i],这时候i,j左面的值都小于等于右侧,所以i和j是我们要的值。b: nums1[i] <nums2[j-1] 说明i对应的元素偏小,i需要向右移动。 c: nums1[i-1]>nums2[j] 说明i-1的值偏大,需要向左移动i.重复第一步重新确定i。因为两个数组升序,所以每次比较都用nums1[i]或者nums1[i-1]和nums2[j]或者nums2[j-1]对应比较,而不用和本身比较。
3.确定好了i和j的值之后,找出中位数。如果大数组的长度是奇数,那么中位数是 max(nums1[i-1],nums2[j-1]),也就是大数组左半部分最大值。 如果大数组长度是偶数,那么中位数是(max(nums1[i-1],num2[j-1]) + min(nums1[i],nums2[j]))/2,也就是左半部分最大值和右半部分最小值的平均值。
这个问题还有几种情况。
1 如果数组nums1的长度远大于nums2的长度,那么设定i的值之后,计算出来的j的值可能会越界,解决方法就是永远把长度较短的数组放前面,i从较短的数组来取。
2 如果数组nums1的长度小于nums2的长度,且nums1所有的元素都大于nums2。 这种情况i=0为止也没找到合适的值,这时候我们跳出二分查找的循环,所求的中位数是nums2[j-1](仅奇数情况)
3 如果数组nums1的长度小于nums2的长度,且nums1所有的元素都小于nums2。这种情况i=nums1.size()-1为止也没找到合适的值,这时候我们跳出二分查找的循环,所求的中位数是max(nums1[i-1],nums2[j-1])(仅奇数情况).
最终代码如下
本文是通过学习 微信公众号 程序员小灰 的文章来进行总结的。