#PS:不明之处,请君留言,以期共同进步!
##1、题目描述
给定两个大小为 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
##2、代码实现
###2.1 C++语言
参考文档:【分步详解】两个有序数组中的中位数和Top K问题
//O(log(m+n))
class Solution {
public:
double findMedianSortedArrays(vector& nums1, vector& nums2)
{
int nums1Size = nums1.size();
int nums2Size = nums2.size();
if (nums1Size > nums2Size) //保证数组1一定最短
return findMedianSortedArrays(nums2, nums1);
int L1, L2, R1, R2, C1, C2, low = 0, high = 2 * nums1Size; //虚拟加了'#'所以数组1是2*n+1长度
while (low <= high) //二分
{
C1 = (low + high) / 2; //C1是二分的结果
C2 = nums1Size + nums2Size - C1; //关键点
L1 = (C1 == 0) ? INT_MIN : nums1[(C1 - 1) / 2]; //nums1整体比中位数大,只能选L2
R1 = (C1 == 2 * nums1Size) ? INT_MAX : nums1[C1 / 2]; //nums1整体比中位数小,只能选R2
L2 = (C2 == 0) ? INT_MIN : nums2[(C2 - 1) / 2]; //nums2整体比中位数大,只能选L1
R2 = (C2 == 2 * nums2Size) ? INT_MAX : nums2[C2 / 2]; //nums2整体比中位数小,只能选R1
if (L1 > R2)//C1割太大,左移
high = C1 - 1;
else if (L2 > R1)//C1割太小,右移
low = C1 + 1;
else//找到合适的割,即在两个数组中,整个左部小于整个右部
break;
}
return (max(L1, L2) + min(R1, R2)) / 2.0;
}
};
###2.2 C语言(3种)
####第1种
//O(m+n)
//合并两有序数组成一个新有序数组,再按中间位置取值
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size)
{
int nums12Size = nums1Size + nums2Size;
//int *nums12[nums12Size]; //C语言不支持
int *nums12 = (int*)malloc(sizeof(int) * nums12Size);
int i = 0, j = 0, k = 0;
while(i < nums1Size && j < nums2Size)
{
if(nums1[i] < nums2[j])
{
nums12[k] = nums1[i];
++i;
++k;
}
else
{
nums12[k] = nums2[j];
++j;
++k;
}
}
while(i < nums1Size)
{
nums12[k] = nums1[i];
++i;
++k;
}
while(j < nums2Size)
{
nums12[k] = nums2[j];
++j;
++k;
}
double middle;
if(nums12Size % 2 == 0)
middle = (nums12[nums12Size / 2 - 1] + nums12[nums12Size / 2]) * 1.0 / 2;
else
middle = nums12[nums12Size / 2];
return middle;
}
####第2种
//O(k)
//两个指针分别从两数组开头指,比较两指针处的数,谁小谁往后移,
//直到两指针共移动k次,k为中间位置
//可以理解成当前下标前面的元素都是归并有序的,而不包括当前下标所指向的元素
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size)
{
int nums12Size = nums1Size + nums2Size;
int index1 = 0; //移动次数和下标,刚好是一一对应关系
int index2 = 0;
double middle = 0;
while (index1 + index2 < (nums12Size + 2) / 2 ) //移动次数之和小于K
{
if (index1 < nums1Size) //移动数组1
{
while (index2 == nums2Size || nums1[index1] <= nums2[index2])
{
index1++;
//无论奇偶都用中间的两个数计算中位数,只是奇数时中间的两个数在同一个位置
if (index1 + index2 == (nums12Size + 1) / 2)
{
middle += nums1[index1 - 1];
}
if (index1 + index2 == (nums12Size + 2) / 2)
{
middle += nums1[index1 - 1];
}
if (index1 == nums1Size) //数组1整体比中位数小
{
break;
}
}
}
if (index2 < nums2Size) //移动数组2
{
while (index1 == nums1Size || nums2[index2] <= nums1[index1])
{
index2++;
if (index1 + index2 == (nums12Size + 1) / 2)
{
middle += nums2[index2 - 1];
}
if (index1 + index2 == (nums12Size + 2) / 2)
{
middle += nums2[index2 - 1];
}
if (index2 == nums2Size) //数组2整体比中位数小
{
break;
}
}
}
}
return middle / 2;
}
####第3种
//O(log(m+n))
//两个数组分别采用二分法查找
//寻找第K小的数
//与C++代码的二分有所区别
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size)
{
int nums12Size = nums1Size + nums2Size;
int left = (nums12Size + 1) / 2; //左中位数的位置,位置是从1开始的,下标是从0开始的
int right = (nums12Size + 2) / 2; //右中位数的位置
if (left == right) //奇数
{
return getKMin(nums1, nums1Size, 0, nums2, nums2Size, 0, left);
}
else//偶数
{
return (getKMin(nums1, nums1Size, 0, nums2, nums2Size, 0, left) + \
getKMin(nums1, nums1Size, 0, nums2, nums2Size, 0, right)) * 1.0 / 2;
}
}
//注意:k和数组都是递归变化的
int getKMin(int* nums1, int nums1Size, int nums1Start, int* nums2, int nums2Size, int nums2Start, int k)
{
if (nums1Start > nums1Size - 1) //数组1整体比中位数小,中位数在数组2中
{
return nums2[nums2Start + k - 1];
}
if (nums2Start > nums2Size - 1) //数组2整体比中位数小,中位数在数组1中
{
return nums1[nums1Start + k - 1];
}
if (k == 1) //要找的是第1小的数,分两种情况,一种是两个数组共有2个以内的元素,另一种是递归到k=1
{
return nums1[nums1Start] < nums2[nums2Start] ? nums1[nums1Start] : nums2[nums2Start];
}
int nums1Min = INT_MAX, nums2Min = INT_MAX;
if (nums1Start + k / 2 - 1 < nums1Size) //当一个数组很短,比中位数的位置还短时,就先不管它
{
nums1Min = nums1[nums1Start + k / 2 - 1];
}
if (nums2Start + k / 2 - 1 < nums2Size)
{
nums2Min = nums2[nums2Start + k / 2 - 1];
}
//每次都从“较小”的数组中“删掉”前k/2个元素,直到将其中的一个数组全部删掉,或者k=1时,就能在下一次递归中找到目标元素
return nums1Min < nums2Min ? \
getKMin(nums1, nums1Size, nums1Start + k / 2, nums2, nums2Size, nums2Start, k - k / 2) : \
getKMin(nums1, nums1Size, nums1Start, nums2, nums2Size, nums2Start + k / 2, k - k / 2);
}