LeetCode 4 两个排序数组的中位数 / Median of Two Sorted Arrays

题目描述:

  这题还是有点难度的,想了比较久,最开始的时候没看见复杂度要求,心想不就是把两个数组合并起来就可以了么,为什么tag是困难,结果看见复杂度要求,才知道要二分做,一开始二分又想错了方向,尝试建立两个数组各自的中位数的联系,结果写了一大堆发现自己走错了路,最后参考别人的思路才做出。

解题思路:本题核心函数就一个: double findKth(vector<int> &nums1, int i, vector<int> &nums2, int j, int k)

表示找到两个排序数组最后合并排序后的第k个数,其中 int i和int j表示还没搜索过的元素的起点(比如初始的时候i和j都等于0)

那么怎么在没有合并的情况下,怎么找到第k个数呢?

思路是这样的:判断 nums1 数组中第k/2个数 和 nums2 数组中第k/2个数的大小关系,比如 nums1[k/2-1] < nums2[k/2-1],那么第k个数肯定不在nums1[0] 到 nums1[k/2-1]这个区间内,因此,直接把nums1[0] 到 nums1[k/2-1]这个区间排除,在对剩下元素的递归处理。这样说可能有点抽象,举个具体的例子来说明下。

比如:nums1 里面有 10 个元素,nums2里面也有 10 个元素,现在问题要求第16个元素是多少( k = 16)?

首先,k/2 = 8 ,找到nums1中第8个元素nums1[7],和nums2中第k - k/2 = 8个元素nums2[7],如果nums1[7]<nums2[7],那么答案肯定不在nums1[0]到num1[7]之间,因为即使最坏的情况,nums2[0]到nums2[6]全在nums1[0]到nums1[7]里面,那么这里面一共也才15个元素 < (k = 16),所以直接把nums1[0]到nums1[7] 排除掉,对剩下的elems递归处理。

本题的核心算法就是上面一段,还有个难点就是关于坐标的换算,我主要就是卡在了这个地方,具体看以下代码,可以结合上一段举的例子来理解:

class Solution {
	public:
		/**
		 * @param A: An integer array.
		 * @param B: An integer array.
		 * @return: a double whose format is *.5 or *.0
		 */
		double findMedianSortedArrays(vector<int> A, vector<int> B) {
			// write your code here
			int sizeA = A.size(), sizeB = B.size();
			if (sizeA <= 0 && sizeB <= 0) {
				return 0;
			}

			int total = sizeA + sizeB;
			if (total % 2 == 1) {
				return findKth(A, 0, B, 0, total / 2 + 1);
			}
			else {//偶数则取中间两个值的平均数
				return (findKth(A, 0, B, 0, total / 2) + findKth(A, 0, B, 0, total / 2 + 1)) / 2;
			}
		}
		
		double findKth(vector<int> &nums1, int i, vector<int> &nums2, int j, int k) {
			//一定要保证nums1的长度小于nums2的长度,不然对nums2的访问可能会越界
			if (nums1.size() - i > nums2.size() - j) {
				return findKth(nums2, j, nums1, i, k);
			}
			// 判断小的数组是否为空,为空的话,直接在另一个数组找第K个即可
			if (nums1.size() == i) {//递归结束情况1
				return nums2[j + k - 1];
			}
			// 当K = 1时,表示我们要找第一个元素,只要比较两个数组的第一个元素,返回较小的那个即可
			if (k == 1) {//递归结束情况2
				return min(nums1[i], nums2[j]);
			}
            //pa表示在nums1中定位的元素的坐标加上一(下面访问的时候会减掉),pb同理
			int pa = min(i + k / 2, int(nums1.size())), pb = j + k - (pa - i);//nums1中的元素不够k/2的话,那么就有多少取多少(nums1.size())                                                  //pa - i :在nums1中已取得元素的个数                                                                          //k - (pa - i):剩下的还需要在nums2中取得元素的个数
			
			if (nums1[pa - 1] < nums2[pb - 1]) {
				return findKth(nums1, pa, nums2, j, k - pa + i);
			}
			else if (nums1[pa - 1] > nums2[pb - 1]) {
				return findKth(nums1, i, nums2, pb, k - pb + j);
			}
			else {
				return nums1[pa - 1];//相等的话说明说明这就是答案,直接返回即可
			}
		}
	};
还有为什么要二分呢,其实你nums1中取k/3个,nums2中取2k/3个,然后比较nums1[k/3-1]和nums2[2k/3-1]也可以,因为明显二分最快。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值