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

本文详细介绍了如何在O(log(m+n))的时间复杂度内找到两个正序数组的中位数。首先,通过归并排序的方法进行解析,虽然此方法不符合时间复杂度要求。接着,提出了双指针移动的思路,但同样不满足时间复杂度限制。最后,给出了官方解答——使用二分法寻找第k小数,成功实现了O(log(m+n))的时间复杂度。这种方法通过不断排除较小元素,最终找到中位数。
摘要由CSDN通过智能技术生成
  • 题目描述:

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数
算法的时间复杂度应该为 O(log (m+n))

  • 题目难度:困难

(虽然,第一次做的时候,是用归并思路做的,但是归并不符合题意,时间复杂度是O(m+n),而要求是O(log(m+n)), 这就有点难了!!哈哈)

示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

  • 思路一:归并

首先,题目给的是两个正序的数组nums1nums2,也就是说都是单调递增的序列;其次,示例也给提示了,合并数组后,就很容易判断出中位数是多少。显然,归并做法的时间复杂度是
O(m+n),不符合题意,但我还是放出来了。

class Solution{
public:
	double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
		int p1 = 0, p2 = 0;//p1,p2分别指向nums1,nums2
		vector<int>& nums(nums1.size() + nums2.size(), 0);//辅助数组
		int p = 0;//p指向辅助数组
		//归并
		while (p1 < nums1.size() && p2 < nums2.size()) {
			nums[p++] = nums1[p1] <= nums2[p2] ? nums1[p1++] : nums2[p2++];
		}
		while (p1 < nums1.size()) nums[p++] = nums1[p1++];
		while (p2 < nums2.size()) nums[p++] = nums2[p2++];
		//注意:此时p记录的位置是什么?
		//无论最后nums1和nums2谁长还是一样长,最后p都会自增一次
		//所以,p代表的是nums数组的size
		//判读nums奇数长度还是偶数长度
		if (p % 2 == 0) return (double)((nums[p / 2] + nums[p / 2 - 1]) / 2);//注意精度
		else return (double)nums[p / 2];
	}
}
  • 思路二:双指针移动

用两个指针分别指向两个数组,谁小谁移动,奇数情况下,直到移动到len/2 + 1的位置;偶数情况下,移动到len/2, len/2 + 1

  • 时间复杂度:O(m + n) (同样不符合题意!!!哈哈哈)

代码如下:

class Soluton {
public:
	double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
		int l1 = nums1.size(), l2 = nums2.size();
		//p1-->nums1, p2-->nums2
		//pivot1,pivot2 保存可能是中位数的值
		int p1 = 0, p2 = 0, pivot1 = 0, pivot2 = 0;
		//一共移动 (l1 + l2) / 2 + 1 就刚好到了中位数的位置
		//移动规则:谁小就移动谁,知道移动 (l1 + l2) / 2 次
		for (int i = 0; i <= (l1 + l2) / 2; ++i) {
			//p1相当于慢指针的意思,用来应对偶数情况
			pivot1 = pivot2;
			pivot2 = (p1 < l1 && (p2 >= l2 || nums[p1] < nums[p2])) ? nums1[p1++] : nums2[p2++];//理解如下
		}
		return (l1 + l2) & 1 ? pivot2 : (pivot1 + pivot2) / 2.0;
	}
}

/*
	几个需要注意的地方:
	1. pivot2 = (p1 < l1 && (p2 >= l2 || nums[p1] < nums[p2])) ? nums1[p1++] : nums2[p2++];
		等价于:
		if (p1指针指到了“尾”) { // p1 < l1 不成立
    		只能移动 p2 指针
		} else if (p2指针指到了“尾”) { // p2 >= l2 成立
    		只能移动 p1 指针
		} else { // p1指针和p2指针都没指到“尾”
    		if (p1指针指向的数 > p2指针指向的数) 
        		移动 p2 指针
    		else // p1指针指向的数 <= p2指针指向的数
        		移动 p1 指针
		}
	2. (l1 + l2) & 1 表示 l1 + l2 是否为奇数,奇数结果为1,否则为0
	3. pivot1 = pivot2 这句存在的意义?
		每次pivot1都会记录pivot2上轮比较结束之后的结果,只有当 l1 + l2 是奇数的情况下pivot1才有用
			
*/
  • 思路三:第k小数(官方解答)

时间复杂度为O(log(m+n))的算法,看到log就能想到二分法,所以思路二就是用二分法。

  • 思路:
如现在有两个数组:
A:1,3,4,9
B:1,2,3,4,5,6,7,8,9

很明显,它们的中位数是 4 

解法:
1. 找中位数,可以看成找两个数组第 K 小的数,这里找第几 K 取决于 A,B 的长度以及他们总长度的奇偶;
2. 首先,寻找两个数组中下标为 K / 2 - 1 的数,比较他们的大小;小的数的左边的数就必定不是第 K 小的数;这时候,就可以排除这几个数,转而去寻找 K - (排除的数) 小的数;然后,重复这个过程;
3. 为什么比较两个数组下标为 K/2 - 1 的数,较小的数组就可以排除呢?

第k小数

class Solution {
public:
int getKthElement(vector<int>& nums1, vector<int>& nums2, int k) {
	int l1 = nums1.size(), l2 = nums2.size();
	int index1 = 0, index2 = 0;

	while (true) {
		int newIndex1 = min(index1 + k / 2 - 1, l1 - 1);
		int newIndex2 = min(index2 + k / 2 - 1, l2 - 1);
		//这里需要减 1,因为这里是对应到数组的下标
		int pivot1 = nums1[newIndex1];
		int pivot2 = nums2[newIndex2];
		if (pivot1 <= pivot2) {
			k -= newIndex1 - index1 + 1;
			// newIndex - index + 1 表示要去除的数
			index1 = newIndex1 + 1;
			// 更新 新的index
		}else {
			k -= newIndex2 - index2 + 1;
			index2 = newIndex2 + 1; 
		}
	}
}

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
 	int totalLength = nums1.size() + nums2.size();
 	if (totalLength % 2 == 0) { //偶数
 		return (getKthElement(nums1, nums2, totalLength / 2) 
 			+ getKthElement(nums1, nums2, totalLength / 2 + 1)) / 2.0;
 		}else { //奇数
 			return getKthElement(nums1, nums2, (totalLength + 1) / 2);
 		}
	}
}
LeetCode 4.寻找两个正序数组的中位数
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值