From: https://leetcode-cn.com/problems/median-of-two-sorted-arrays/
给定两个大小为 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
---------------------------------------------------解题分析-分隔线----------------------------------
解题思路:
- 存在唯一解, 可用逆向思维解题, 从结果推导所求中位数应满足的条件
- 求数组中位数, 需考虑数组大小的奇偶, 先假定数组大小为偶数(奇数时可同比求得)
如下分别以 i, j 拆分 nums1, nums2 :
[nums1(0), nums1(1)...nums1(i),nums1(i + 1)...nums1(m - 1)]
[nums2(0), nums2(1)...nums2(j),nums2(j + 1)...nums2(n - 1)]
则唯一解中存在 i, j , 使得
-
即唯一解特征为:
存在一个 i , 使得
且满足条件:
-
-------------------------------------------------------源码实现-分隔线--------------------------------------------------------
C++ 实现如下:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
//优先处理空数组
if (nums1.empty())
{
int iSize = nums2.size();
if (iSize % 2)
{
return nums2[iSize / 2];
}
else
{
return 0.5 * (nums2[iSize / 2] + nums2[iSize / 2 - 1]);
}
}
else if (nums2.empty())
{
int iSize = nums1.size();
if (iSize % 2)
{
return nums1[iSize / 2];
}
else
{
return 0.5 * (nums1[iSize / 2] + nums1[iSize / 2 - 1]);
}
}
int iSize1 = nums1.size();
int iSize2 = nums2.size();
int iTotal = iSize1 + iSize2 + 4;
/* + 4 可以处理边界问题: 数组拆分出一个空数组的情况
如没有 +4 则需考虑 i = -1 , i = iSize1 -1, j = -1, j = iSize2 - 1 的情况即可
*/
bool bFlag = iTotal % 2;//Size 奇偶标记
int iTotalHalf = (iTotal * 0.5) - 2;
bool bF1, bF2, bG1, bG2;//拆分出空数组的标记
int a1, a2, b1, b2;//对应 nums1[i],nums1[i+1],nums2[j],nums2[j+1]
int j;
for (int i = 0; i <= iSize1; ++i)
{
j = iTotalHalf - i;
if (j < 0 || j > iSize2) continue;
bF1 = false;
bF2 = false;
if (i == 0)
{
bF1 = true;
a2 = nums1[0];
}
else if (i == iSize1)
{
bF2 = true;
a1 = nums1[iSize1 - 1];
}
else
{
a1 = nums1[i - 1];
a2 = nums1[i];
}
bG1 = false;
bG2 = false;
if (j == 0)
{
bG1 = true;
b2 = nums2[0];
}
else if (j == iSize2)
{
bG2 = true;
b1 = nums2[iSize2 - 1];
}
else
{
b1 = nums2[j - 1];
b2 = nums2[j];
}
if (bF1) a1 = b1;
if (bF2) a2 = b2;
if (bG1) b1 = a1;
if (bG2) b2 = a2;
if (a1 <= b2 && b1 <= a2)
{
if (bFlag)
{
return a2 < b2 ? a2 : b2;
}
else
{
return 0.5 * ((a1 > b1 ? a1 : b1) + (a2 < b2 ? a2 : b2));
}
}
}
return 0;//理论上不会执行到这儿
}
-----------------------------------------------------------------改进思路-分隔线-----------------------------------------------------
- 以上实现, 算法复杂度为O(m + 1), 算法查找速度完全依赖于
nums1数组大小m的值 (注:如 n 远小于 m ,考虑使用 j 来计算 i, 则算法复杂度相对用 i 计算 j 会好一点, 也可以设计算法复杂度为 O( Min(m + 1, n + 1)) 程序)
思考 i, j 为
线性关系:
, 可以根据 j 的取值范围
再限定
可加入二分查找, 快速查找 i, 使得算法复杂度为 O(log(m + 1))
二分查找思路:
-
条件1不满足, 则 i 偏大, 条件2不满足, 则 i 偏小. 且不存在 两个条件同时不满足的情况[可反证]
-------------------------------------------------------源码实现-分隔线--------------------------------------------------------
C++ 实现如下:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
//优先处理空数组
if (nums1.empty())
{
int iSize = nums2.size();
if (iSize % 2)
{
return nums2[iSize / 2];
}
else
{
return 0.5 * (nums2[iSize / 2] + nums2[iSize / 2 - 1]);
}
}
else if (nums2.empty())
{
int iSize = nums1.size();
if (iSize % 2)
{
return nums1[iSize / 2];
}
else
{
return 0.5 * (nums1[iSize / 2] + nums1[iSize / 2 - 1]);
}
}
int iSize1 = nums1.size();
int iSize2 = nums2.size();
int iTotal = iSize1 + iSize2 + 4;
bool bFlag = iTotal % 2;
int iTotalHalf = (iTotal * 0.5) - 2;
bool bF1, bF2, bG1, bG2;
int a1, a2, b1, b2;
int j;
//计算i范围
int iFront = 0.5 * (iSize1 - iSize2);
if (iFront < 0) iFront = 0;
int iTail = 0.5 * (iSize1 + iSize2);
if (iTail > iSize1) iTail = iSize1;
int i;
for (;iFront <= iTail;)
{
i = (iFront + iTail) * 0.5;//i 做 二分取值
j = iTotalHalf - i;
if (j < 0 || j > iSize2) continue;
bF1 = false;
bF2 = false;
if (i == 0)
{
bF1 = true;
a2 = nums1[0];
}
else if (i == iSize1)
{
bF2 = true;
a1 = nums1[iSize1 - 1];
}
else
{
a1 = nums1[i - 1];
a2 = nums1[i];
}
bG1 = false;
bG2 = false;
if (j == 0)
{
bG1 = true;
b2 = nums2[0];
}
else if (j == iSize2)
{
bG2 = true;
b1 = nums2[iSize2 - 1];
}
else
{
b1 = nums2[j - 1];
b2 = nums2[j];
}
if (bF1) a1 = b1;
if (bF2) a2 = b2;
if (bG1) b1 = a1;
if (bG2) b2 = a2;
if (a1 > b2)
{
if (bG2)//特殊情况判断, 此状态下需更为移动 iFront
{
if (iFront == i)
{
iFront = iTail;
continue;
}
iFront = i;
continue;
}
if (iTail == i)
{
iTail = iFront;
continue;
}
iTail = i;
}
else if (b1 > a2)
{
if (iFront == i)
{
iFront = iTail;
continue;
}
iFront = i;
}
else//if (a1 <= b2 && b1 <= a2)
{
if (bFlag)
{
return a2 < b2 ? a2 : b2;
}
else
{
return 0.5 * ((a1 > b1 ? a1 : b1) + (a2 < b2 ? a2 : b2));
}
}
}
return 0;
}