【算法百题之三十一】寻找两个正序数组的中位数

【算法百题之三十一】寻找两个正序数组的中位数

 

    大家好,我是Lampard~~

    很高兴又能和大家见面了,接下来准备系列更新的是算法题,一日一练,早日升仙!

    今天的问题是:

    

  思路:1.原本是正序,2.要求时间复杂度是log(n + m).。

通过这两点我们就可以确定我们需要使用二分查找法来实现了。题目要求求中位数,我们看中位数的定义:

设有N个连续偶数,由中位数定义: 当N为奇数时,处于中间位置的变量值即为中位数; 当N为偶数时,中位数则为处于中间位置的2个变量值的平均数.设有N个连续偶数,由中位数定义:
当N为奇数时,处于中间位置的变量值即为中位数;
当N为偶数时,中位数则为处于中间位置的2个变量值的平均数.

 其实这道题再往下解析,其实就是找第K小值的问题。当总长度是奇数是,K为总长度 / 2 + 1,若总长度为偶数的话,需要就需要把K = 总长度 / 2 + 1 和K = 总长度 / 2 + 1的解求出来求平均数。

这里我们可以用一个巧妙的方法:

int len1 = num1.size();
int len2 = num2.size();
int key1 = (len1 + len2 + 1) / 2;
int key2 = (len1 + len2 + 2) / 2;

我们只需要把key1和key2的解求平均,那么不管总长度是奇数还是偶数,得到的都是我们想要的答案。那么这样子就简单多了,剩下的问题就是写好我们的二分法就可以了:

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
	int m = nums1.size();
	int n = nums2.size();
	// 中位数 = (left + right)/2
	int left = (m + n + 1) / 2;
	int right = (m + n + 2) / 2;
	return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;
}

首先说一下我们的递归过程:现在两个数组都是有序的,我们需要第K大的值,那么我们可以一直把K给除2,如果num1【第k/2个元素】< num2【第k/2个元素】,则说明数组1中的前k/2个元素不可能成为第k个元素的候选,反之亦然

这时可能会有疑问,为啥不可能是第k个元素呢?

因为我们现在是取向量1和向量2的前k / 2 个元素,有可能是第K大的数只可能是num1【第k/2个元素】 和 num2【第k/2个元素】,但是现在num1【第k/2个元素】< num2【第k/2个元素】,那么num1【第k/2个元素】已经比num2的小,人家已经比你大了,那么num1已经丧失了成为第k大的可能性,最多就是第k-1大,所以其前面的所有元素都不可能成为第k个元素的候选。


此时我们就舍弃num1的前k/ 2个元素,为了不新创向量浪费时间和空间,我们则用一个下标i, j来记录num1和num2开始的下标,所以我们舍弃前k / 2的操作可以通过对i赋值成k / 2 来实现。就这样一直递归下去,当我们的K=1时候,对num1【i】和num2【j】的大小进行判断就可以知道答案了。

此时会有大神注意到,这样不行啊,程序不健壮,若num1长度很短,num2的长度很大,导致num1【k / 2】直接越界那怎么办呢?

当某一个数组的起始位置大于等于其数组长度时,说明其所有数字均已经被淘汰了,相当于一个空数组了,那么实际上就变成了在另一个数组中找数字,直接就可以找出来了。

// 若nums1为空(或是说其中数字全被淘汰了)
// 在nums2中找第k个元素,此时nums2起始位置是j,所以是j+k-1
if (i >= nums1.size())    return nums2[j + k - 1];
// nums2同理
if (j >= nums2.size())    return nums1[i + k - 1];

ps:赋予整型最大值的意思只是说如果第一个数组的K/2不存在,则说明这个数组的长度小于K/2,那么另外一个数组的前K/2个我们是肯定不要的。例如,加入第一个数组长度是2,第二个数组长度是12,则K为7,K/2为3,因为第一个数组长度小于3,则无法判断中位数是否在其中,而第二个数组的前3个肯定不是中位数!

实现的代码:

int findKth(vector<int>& nums1, int i, vector<int>& nums2, int j, int k)
{
	// 若nums1为空(或是说其中数字全被淘汰了)
	// 在nums2中找第k个元素,此时nums2起始位置是j,所以是j+k-1
	if (i >= nums1.size())    return nums2[j + k - 1];
	// nums2同理
	if (j >= nums2.size())    return nums1[i + k - 1];

	// 递归出口
	if (k == 1)  return nums1[i] < nums2[j] ? nums1[i] : nums2[j];

	// 这两个数组的第K/2小的数字,若不足k/2个数字则赋值整型最大值,以便淘汰另一数组的前k/2个数字
	int midVal1 = (i + k / 2 - 1 < nums1.size()) ? nums1[i + k / 2 - 1] : INT_MAX;
	int midVal2 = (j + k / 2 - 1 < nums2.size()) ? nums2[j + k / 2 - 1] : INT_MAX;

	// 二分
	if (midVal1 < midVal2)
		return findKth(nums1, i + k / 2, nums2, j, k - k / 2);
	else
		return findKth(nums1, i, nums2, j + k / 2, k - k / 2);
}

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
	int m = nums1.size();
	int n = nums2.size();
	// 中位数 = (left + right)/2
	int left = (m + n + 1) / 2;
	int right = (m + n + 2) / 2;
	return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;
}

测试函数以及测试结果:

OK,今天的博客就到这里,谢谢大家!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lampard杰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值