力扣算法第四题—查找两个正序数组的中位数
题意:给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。算法的时间复杂度应该为 O(log (m+n)) 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
解析
按题意要求,算法复杂度为O(log (m+n)) ,这里肯定就需要用到二分查找算法。二分查找算法的时间复杂度为O(m)。由于num1和num2数组都是正序数组,我们可以在第一个和第二数组中间划一条线,保证左右两边元素个数相等,如果两个数组元素之和为奇数,则左边区域多放一个元素。假设第一个数组nums1有4个元素,第二个数组nums2有5个元素,则第一次划线的过程中,第一个数组的左边放2个元素(对应下表为0,1),第二个数组的左边放3个元素(对应小下表为0,1,2),设第一个数组划线的右边第一个元素下标为i,则i刚好对应着第一个元素左边的元素个数,第二个数组划线的右边的第一个元素下表为j,则对应着第二个数组左边的元素个数,同时满足i+j等于两个数组元素个数之和的一半(如果两个元素个数分别为m,n,则i+j = (m+n+1)/2,这里需要考虑奇数情况)。
当第一次划线切割之后,判断第一个数组的左边最大元素是否小于第二个数组的右边的最小元素,即需要判断nums1[i-1]是否小于nums2[j],如果满足,则第一个数组的分割线就需要往右移,即在第一个数组的[i,m]中进行二次查找,直到遍历完成结束。但遍历过程中需要考虑几种特殊情况。
第一:如果第一个数组切割之后左边已经没有元素
第二:第一个数组切割之后右边已没有元素
第三:第二个数组切割之后左边没有元素
第四:第二个数组切割之后右边没有元素
这四种场景需要做特殊判断处理。
```java
package likou;
/*
* 寻找两个正序数组中的中位数
* 给定两个大小分别为m和n的正序(从小到大)数组num1和num2。请找出并返回两个正序数组的中位数
* 算法复杂度为O(log(m+n))
*/
/*
* 算法解析:
* 如果要求复杂度为O(log(m+n)),这里肯定就要想到用二分查找
*/
public class Solution04 {
public static void main(String args[]) {
int nums1[] = {}, nums2[] = {1};
Solution04 solution = new Solution04();
double value = solution.findMedianSortedArrays(nums1, nums2);
System.out.println(value);
}
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int nums1_length = nums1.length;
int nums2_length = nums2.length;
if(nums1_length > nums2_length ){
int[] temp = nums1;
nums1 = nums2;
nums2 = temp;
}
//在第一个数组的[0,nums1_length]空间内找到中间点
int left = 0;
int right = nums1.length;
int totalleft =(nums1_length + nums2_length +1)/2;
while(left < right) {
int i = left + (right - left + 1)/2;
int j = totalleft - i;
if(nums1[i-1] > nums2[j]) {
right = i-1;
}else {
left = i;
}
}
int i = left;
int j = totalleft - i;
int nums1_leftMax = i==0?Integer.MIN_VALUE:nums1[i-1];
int nums1_rightMin = i==nums1.length? Integer.MAX_VALUE:nums1[i];
int nums2_leftMax = j==0?Integer.MIN_VALUE:nums2[j-1];
int nums2_rightMin = j==nums2.length? Integer.MAX_VALUE:nums2[j];
if((nums1.length + nums2.length)%2 == 1) {
return Math.max(nums1_leftMax, nums2_leftMax);
}else {
return (double)(Math.max(nums1_leftMax, nums2_leftMax)+Math.min(nums1_rightMin, nums2_rightMin))/2;
}
}
}