LeetCode|C++|4. Median of Two Sorted Arrays

本文介绍了一种高效查找两个已排序数组中位数的方法,通过Top-K思想递归地排除不必要的元素,达到O(log(m+n))的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

中位数:一组有序数字里面最中间的那个数 (奇数个数)或者最中间两个数的平均数(偶数个数)

三种方法:

1、首先想到的是把两个数组按升序merge起来,直接取最中间的那个数(或者中间两个数的平均数),时间复杂度O(m+n)

2、第一种方法的缺点是要把所有元素都进行排序才能直接取最中位数,但是我们只需要取中位数,其实当排序排到中间那个数的时候后面的元素就不需要排了,我们可以边排序边count元素个数,如果count值等于中位数的位置则停止排序,当前元素即为中位数。此时时间复杂度要根据两个数组的元素分布情况来看了,如果该中位数在数组的很后边,那么时间复杂度也接近于O(m+n)

3、更好的方案是Top-K思想,把中位数看作是第k小的数(升序排列)。

首先假设数组A和B的元素个数都大于k/2,我们比较A[k/2-1]和B[k/2-1]两个元素,这两个元素分别表示A的第k/2小的元素和B的第k/2小的元素。这两个元素比较共有三种情况:

(1)如果A[k/2-1]<B[k/2-1],这表示A[0]到A[k/2-1]的元素都在A和B合并之后的前k小的元素中,也就是说A[k/2-1]不可能大于两数组合并之后的第k小值,那么就可以直接不考虑这部分元素了。

(2)当A[k/2-1]>B[k/2-1]时也类似,这表示B[0]到B[k/2-1]的元素都在A和B合并之后的前k小的元素中,那么就可以直接不考虑这部分元素了。

(3)当A[k/2-1]=B[k/2-1]时,我们已经找到了第k小的数,就是A[k/2-1]或B[k/2-1]。由于在A和B中分别有k/2-1个元素小于它,所以它是第k小的数。

如果其中一个数组(假设是数组A)的元素个数小于k/2,那么我们要比较A[m]和B[k-m],方法同上。

因为要不断删除前k/2个数,直到寻找到第k个数,这里用递归实现寻找第k小的数。

还需要考虑几种情况:

(1)如果A或者B为空,则直接返回B[k-1]或者A[k-1];
(2)如果k为1,我们只需要返回A[0]和B[0]中的较小值;
(3)如果A[k/2-1]=B[k/2-1],返回其中一个;

时间复杂度为O(log(m+n))

class Solution {
public:
	double findKth(vector<int>::iterator arr1, int size1, vector<int>::iterator arr2, int size2, int k){
		if (size1 > size2)
			return findKth(arr2, size2, arr1, size1, k);
		if (size1 == 0)
			return arr2[k - 1];
		if (k == 1)
			return min(arr1[0], arr2[0]);
		int pa = min(k / 2, size1);
		int pb = k - pa;
		if (arr1[pa - 1] < arr2[pb - 1])
			return findKth(arr1 + pa, size1 - pa, arr2, size2, k - pa);
		else if (arr1[pa - 1] > arr2[pb - 1])
			return findKth(arr1, size1, arr2 + pb, size2 - pb, k - pb);
		else
			return arr1[pa - 1];


	}

	double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
		int len1 = nums1.size();
		int len2 = nums2.size();
		int sum = len1 + len2;
		if (sum % 2 != 0)
			return findKth(nums1.begin(), len1, nums2.begin(), len2, sum / 2 + 1);
		else
			return (findKth(nums1.begin(), len1, nums2.begin(), len2, sum / 2)
			+ findKth(nums1.begin(), len1, nums2.begin(), len2, sum / 2 + 1)) / 2;
	}
};

补充一点C++vector的迭代器iterator的知识:

iterator可以很方便遍历vector的元素,可以从指定的某个位置开始遍历(iter+2).

区分两种const iterator:

1、const迭代器,是常量,是不能改变的迭代器,其性质是由const决定的。比如说我们这样定义一个const迭代器。

vector<int> vv(10,9);  
const vector<int> :: iterator iter = vv.begin(); 

当程序中出现如下的语句时是错误的

++iter;  

其原因是iter是一个常量,因此是不能改变的。换句话说,iter只能指向vv的地一个元素,不能指向其他的元素。

但是这样的语句是正确的:

*iter = 10;  

因为虽然iter不能指向其他的元素,但是其指向的第一个元素的值是可以改变的。

2、对于const_iterator来说的话,正好相反。比如我们这样定义

vector<int> vv(10,9);  
vector<int> :: const_iterator iter;  

即定义了一个const_iterator迭代器。这个迭代器是可以自己增加的,但是其所指向的元素是不可以被改变的。比如说

for(iter = vv.begin(); iter != vv.end(); ++iter){  
    cout << *iter << endl;  
}  

这样是正确的,即iter本身的值是可以改变的。但是

for(iter = vv.begin(); iter != vv.end(); ++ iter){  
   *iter = 0;  
}  

这样是不对的,因为const_iterator迭代器是不能改变其所指向的元素的值的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值