leetcode 寻找两个正序数组的中位数 题解实现

From: https://leetcode-cn.com/problems/median-of-two-sorted-arrays/

 

4. 寻找两个正序数组的中位数

给定两个大小为 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 + j =\frac{m + n}{2} - 2 //i \epsilon [0,m); j \epsilon [0, n)
  • nums1[i] \leq nums2[j + 1]
  • nums2[j] \leq nums1[i + 1]​​​​​​​

即唯一解特征为:

存在一个 i , 使得 j =\frac{m + n}{2} - 2 - i //i \epsilon [0,m); j \epsilon [0, n)

且满足条件:

  • nums1[i] \leq nums2[j + 1]
  • nums2[j] \leq nums1[i + 1]​​​​​​​

-------------------------------------------------------源码实现-分隔线--------------------------------------------------------

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 =\frac{m + n}{2} - 2 - i //i \epsilon [0,m); j \epsilon [0, n)  ,  可以根据 j 的取值范围

再限定\frac{m - n}{2} - 2 < i \leqslant \frac{m + n}{2} - 2 

  • ​​​​​​可加入二分查找, 快速查找 i, 使得算法复杂度为 O(log(m + 1))

​​​​​​​二分查找思路: ​​​​​​​

  • 1: nums1[i] \leq nums2[j + 1]
  • 2: nums2[j] \leq nums1[i + 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值