4. Median of Two Sorted Arrays

题目:There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

有两个排好序的数组nums1和nums2,大小分别为m和n。现在要找到两个数组合并之后的中位数,

要求时间复杂度是O(log (m+n)).


思路一:最简单的方法首先想到的是直接合并两个数组,然后排序,直接就能取出中位数。

class Solution {
public:
	double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
		int m = nums1.size();
		int n = nums2.size();
		vector<int>  vec;
		vec.insert(vec.end(), nums1.begin(), nums1.end());
		vec.insert(vec.end(), nums2.begin(), nums2.end());

		sort(vec.begin(), vec.begin() + m + n);
		int k = (m + n) / 2;
		if ((m + n) % 2 == 0)
			return (vec.at(k-1) + vec.at(k)) / 2.0;
		else
			return vec.at(k);
	}
};


利用STL提供的sort函数自动排序,取出合并后数组的中位数,居然通过了。我们已经知道提供的sort函数复杂度为 O( n log n ),显然不符合题目的要求,所以可知这道题的测试用例还不够多。

思路二:看题目要求寻找的是中位数,百度百科定义的中位数如下

中位数是按顺序排列在一起的一组数据中居于中间位置的数,即在这组数据中,有一半的数据比他大,有一半的数据比他小。

注意最后一句话,有一半的数据比他小,从这个点出发可以从头开始数一半的数,就找到中位数了。

class Solution {
public:
	double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
		int m = nums1.size();
		int n = nums2.size();
		int k = (m + n) / 2;
		if ((m + n) % 2)
			return findK(nums1, nums2, m,n,k);
		else
			return (findK(nums1, nums2, m, n, k-1) + findK(nums1, nums2, m, n, k)) / 2.0;
	}
	int findK(vector<int>& nums1, vector<int>& nums2, int m, int n, int k)
	{
		int p1 = 0;
		int p2 = 0;
		int p = 0;
		while (p < k)
		{
			if (p1 >= m && p2 < n)
				p2++;
			else if (p2 >= n && p1 < m)
				p1++;
			else if (nums1[p1] < nums2[p2])
				p1++;
			else
				p2++;
			p++;
		}
		if (p1 >= m && p2 < n)
			return nums2[p2];
		else if (p2 >= n && p1 < m)
			return nums1[p1];
		else if (nums1[p1] < nums2[p2])
			return nums1[p1];
		else
			return nums2[p2];
	}
};


不出意外,这个解法也能通过。这个解法要分别遍历两个数组,所以时间复杂度为 O(n)+O(m)。比第一种思路要快,写起来也简单,只是要特别 注意边界问题,这里花了我不少时间。虽然也侥幸通过了所有测试用例,但是与题目要求的时间复杂度仍有差距。


思路三:其实第二种解法给了我们启发,找一半的数的时候,两两比较两个数组最小的元素,就是在做归并排序。经过老师课上反复强调,突然就领悟到这道题可以用分治的解法。我们最终的目标是找中间的数,已知两个数组的大小分别为m和n,那么就是找排在k=(m+n)/2的数,在大小为2K的区间 内找到第k个数的任务可以变成在大小为K的区间内找到第K/2个数的任务,以此类推,在大小为1的区间内找到的数就是自身。但是这道题有两个数组,并没有合并成一个数组,不能每次都很准确的去掉K/2的区间,因此一次分治只能对某一个数组去除前K/2个数,另一个数组不动。分别找到两个数组排在第K/2的数(注意不要超过数组的大小),比较大小,去掉小的那个数组前面的K/2个数。这样数组的区间不断减小,要找的第K个数也离数组的头越来越近。大约经过O(log(m+n))次操作,就能找到第K个数。

class Solution {
public:
	double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
		int a = nums1.size();
		int b = nums2.size();
		int k = (a + b) / 2;
		if ((a + b) % 2) //总大小为奇数的时候,中位数只有一个
			return findK(nums1, nums2, 0, 0, a, b, k+1);
		else//总大小为偶数的时候,中位数是中间两个数的平均数
			return (findK(nums1, nums2, 0, 0, a, b, k) + findK(nums1, nums2, 0, 0, a, b, k + 1)) / 2;
	}
	double findK(vector<int>& nums1, vector<int>& nums2, int start1, int start2, int size1, int size2, int k)
	{
		if (size1 == 0)//特殊情况处理
			return nums2[start2 + k - 1];
		if (size2 == 0)//特殊情况处理
			return nums1[start1 + k - 1];
		if (k == 1)//当K=1的时候,要找到数就在数组的头
			return min(nums1[start1], nums2[start2]);
		int step1, step2;
		if (size1 < size2)//分别找到两个数组K/2位置的数(注意边界处理)
		{
			step1 = min(size1, k / 2);
			step2 = k-step1;
		}
		else
		{
			step2 = min(size2, k / 2);
			step1 = k - step2;
		}
		if (nums1[start1 + step1 - 1] < nums2[start2 + step2  - 1])//去除一个数组的一部分,变成新的子任务
			return findK(nums1, nums2, start1 + step1, start2, size1 - step1, size2, k - step1);
		else
			return findK(nums1, nums2, start1, start2 + step2, size1, size2 - step2, k - step2);
	}
};


可以看出,代码非常简洁,而且效率也很高。在最好情况下,每次都有k一半的元素被删除,所以算法复杂度为logk,由于求中位数时k为(m+n)/2,所以算法复杂度为log(m+n)。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值