1.题目简述
给定两个大小分别为 和 的的正序数组(从小到大) 和 ,要求返回这两个中序数组的中位数。
示例1
输入:nums1 = [1,2,8,10,12], nums2 = [2,5,6,9,10,16]
输出:8.00000
说明:合并数组 = [1,2,2,5,6,8,9,10,10,12,16],中位数 8
示例2
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
详情请查看 LeetCode 官网:4. 寻找两个正序数组的中位数 - 力扣(LeetCode)
2.方法一:二分查找
- 假设中位数为 k,分别比较 和
- 当 时,说明比 小的元素只有 个,因此可知 中的元素都不是第 个元素,可以直接排除
- 排除后,需要将 减去排除的元素个数,并更新 的开始位置
- 当 时,同理
- 当 时,说明比 小的元素只有 个,因此可知 中的元素都不是第 个元素,可以直接排除
说明:该题解是官方题解,详情请查看:寻找两个有序数组的中位数 - 官方题解 - 力扣(LeetCode)
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int length1 = nums1.length,length2 = nums2.length;
int total = length1 + length2;
if(total%2 == 1){
int midIndex = total/2;
double median = getKthElement(nums1,nums2,midIndex+1);
return median;
}else{
int midIndex = total/2-1,midIndex2 = total/2;
double median = (getKthElement(nums1,nums2,midIndex+1) + getKthElement(nums1,nums2,midIndex2+1)) / 2.0;
return median;
}
}
public int getKthElement(int[] nums1,int[] nums2,int k){
int length1 = nums1.length,length2 = nums2.length;
int index1 = 0,index2 = 0;
while(true){
// 边界情况
if(index1 == length1){
return nums2[index2+k-1];
}
if(index2 == length2){
return nums1[index1+k-1];
}
if(k == 1){
return Math.min(nums1[index1],nums2[index2]);
}
// 正常情况
int half = k / 2;
int newIndex1 = Math.min(index1+half,length1) - 1;
int newIndex2 = Math.min(index2+half,length2) - 1;
int pivot = nums1[newIndex1],pivot2 = nums2[newIndex2];
if(pivot <= pivot2){
// 将 nums1[index1..newIndex1] 之间的数字删掉
// 因为比第 k 个元素小;同时更新 k 元素,因为两个数组中的元素减小
k -= (newIndex1-index1) + 1;
index1 = newIndex1 + 1;
}else{
k -= (newIndex2-index2) + 1;
index2 = newIndex2 + 1;
}
}
}
示例1 图解:
3.方法二:双指针
- 定位中位数是第几个元素
- 定义两个指针,同两个有序数组排序的处理方式相同,所指元素更小的指针向前移动
- 在移动过程中,记录目前已排序元素的个数;当达到中位数所在位置时,记录中位数
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length,n = nums2.length;
// 是否都已找到中位数;true 表示未找到;false 表示已找到
boolean flag = true;
// 记录总长度
int total = m+n;
// 记录中位数个数
int medianNum = total%2==1 ? 1:2;
// 当前排序数组中的索引
int nowNum = -1;
// left1 是 nums1 指针;left2 是 nums2 指针
int left1 = 0,left2 = 0;
// 记录中位数
double sum = 0.0;
while(left1<m && left2<n && flag){
int num = Math.min(nums1[left1],nums2[left2]);
if(nums1[left1] < nums2[left2]){
left1++;
}else{
left2++;
}
nowNum++;
if(total%2==1 && nowNum==total/2){
sum += num;
flag = false;
}else if(total%2==0 && (nowNum==total/2-1||nowNum==total/2)){
sum += num;
if(nowNum == total/2) flag=false;
}
}
while(left1<m && flag){
nowNum++;
if(total%2==1 && nowNum==total/2){
sum += nums1[left1];
flag = false;
}else if(total%2==0 && (nowNum==total/2-1||nowNum==total/2)){
sum += nums1[left1];
if(nowNum == total/2) flag=false;
}
left1++;
}
while(left2<n && flag){
nowNum++;
if(total%2==1 && nowNum==total/2){
sum += nums2[left2];
flag = false;
}else if(total%2==0 && (nowNum==total/2-1||nowNum==total/2)){
sum += nums2[left2];
if(nowNum == total/2) flag=false;
}
left2++;
}
return sum/medianNum;
}