LeetCode 第4题:寻找两个正序数组的中位数
题目描述
给定两个大小分别为m和n的正序数组(从小到大)数组nums1和nums2。请你找出并返回这两个正序数组的中位数。要求算法的时间复杂度应该为O(log(m+n))。
难度:困难
题目链接:4. 寻找两个正序数组的中位数 - 力扣(LeetCode)
示例一:
输入:nums1 = [1,3], nums2 = [2] 输出:2.00000 解释:合并数组 = [1,2,3] ,中位数 2
示例二:
输入:nums1 = [1,2], nums2 = [3,4] 输出:2.50000 解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
提示:
- nums1.length == m
- nums2.length == n
- 0 <= m <= 1000
- 0 <= n <= 1000
- 1 <= m + n <= 2000
- -106 <= nums1[i], nums2[i] <= 106
解题思路:
方法一:二分查找
该题最关键的地方在于时间复杂度要达到O(log(m+n)),故必须使用二分查找,将该问题转化为寻找第k小数的问题。
注意点:
- 中位数的定义
- 当总长度为奇数时,中位数是第(m+n)/2+1个数。
- 当总长度为偶数时,中位数是第(m+n)/2和第(m+n)/2+1个数的平均值。
- 使用二分查找来寻找第k小的数。
- 每次比较两个数组的第k/个数,排除掉不可能的部分。
具体步骤:
- 确定中位数的位置k。
- 在两个数组中进行二分查找:
- 比较两个数组中第k/2个数的大小。
- 较小的那部分不可能是包含第k小的数。
- 排除较小的部分,继续查找剩余部分。
- 递归进行上述过程,直到找到目标数。
时间复杂度:O(log(m+n))
空间复杂度:O(1)
public class Solution
{
public double FindMedianSortedArrays(int[] nums1,int[] nums2)
{
int totalLength = nums1.length+nums2.length;
if(totalLength % 2 ==1)//奇数
{
return FindKth(nums1,0,nums2,0,totalLength/2+1);
}else{ //偶数个元素,注意是除以2.0,保证最后结果除完全
return
(FindKth(nums1,0,nums2,0,totalLength/2)+FindKth(nums1,0,nums2,0,totalLength/2+1))/2.0;
}
}
}
private double FindKth(int[] nums1,int start1,int[] nums2,int start2,int k)
{
//如果数组1已经用完,直接从数组2中返回第k个数
if(start1>=nums1.length) return nums2[start2+k-1];
//如果数组2已经用完,直接从数组1中返回第k个数
if(start2>=nums2.length) return num1[start1+k-1];
//如果k==1,返回两个数组开头较小的数
if(k==1) return Math.Min(nums1[start1],nums2[start2]);
//比较两个数组中第k/2个数的大小
int mid1 = start1+k/2-1<nums1.length ? nums1[start1+k/2-1] : int.MaxValue;
int mid2 = start2+k/2-1<nums2.length ? nums2[start2+k/2-1] : int.MaxValue;
if(mid1<mid2) return FindKth(nums1,start1+k/2,nums2,start2,k-k/2);
else return FindKth(nums1,start1,nums2,start2+k/2,k-k/2);
}
方法二:合并数组法 ---- 不会超时(好理解一些)
将两个正序的数组合并到一个正序数组当中,最后根据整体数组的奇偶性,直接输出中位数。
//定义一个函数findMedianSortedArrays,它接收四个参数,两个数组nums1和nums2,以及它们的长度//nums1Size和num2Size. double findMedianSortedArrays(int* nums1,int nums1Size,int* nums2,int nums2Size) { //定义p1和p2指针,初始位置为数组的开始位置 int p1=0,p2=0,m=0,l=0,k=0,q,n;//m用于存储合并后的数组的长度 //q用于存储从nums1和nums2中取出的元素,n用于存储中位数 int arr[nums1Size + nums2Size]; //arr数组用于存储合并后的数组 while(p1<nums1Size || p2<nums2Size) { if(p1==nums1Size) q=nums2[p2++]; //遍历完nums1 else if(p2==nums2Size) q=nums1[p1++]; //遍历完nums2 else if(nums1[p1]<nums2[p2]) q=nums1[p1++]; //先从小的开始加入arr数组 else q=nums2[p2++]; arr[p1+p2-1] =q;//将q放入arr数组当中,注意-1是根据数组索引 } m=sizeof(arr)/sizeof(int); //or m=nums1Size+nums2Size; if(m==1) return arr[m-1]; //只有一个元素的数组 else{ if(m%2==0) n=(arr[m/2]+arr[m/2-1])/2.0;//偶数个元素 else n=arr[m/2]; //奇数个元素 } return n; }