4.Median of Two Sorted Arrays

本文介绍了一种高效算法来找出两个有序数组的中位数,通过将数组分割并利用二分查找技巧,实现了O(log(min(m,n)))的时间复杂度。

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

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

Example 1:

        nums1 = [1, 3]

        nums2 = [2]

        The median is 2.0

Example 2:

        nums1 = [1, 2]

        nums2 = [3, 4]

        The median is (2 + 3)/2 = 2.5

classSolution {
public:
    doublefindMedianSortedArrays(vector<int>& nums1, vector<int>&nums2) {

    }
};

自己汇编的程序不符合时间复杂度O(log(n+m)),而是O(nlgn)。

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        vector<int> Result(nums1);
		for (auto &c : nums2)
			Result.push_back(c);
		sort(Result.begin(), Result.end());
		if (Result.size() % 2 == 0)
			return (double)(Result[Result.size() / 2 - 1] + Result[Result.size() / 2 - 1]) / 2;
		else
			return (double)Result[Result.size() / 2];
    }
};

我们都知道二分查找的时间复杂度为O(logn),证明如下:

证明:二分查找的基本思想是将n个元素分成大致相等的两部分,去a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果x<a[n/2],则只要在数组a的左半部分继续搜索x,如果x>a[n/2],则只要在数组a的右半部搜索x.

时间复杂度无非就是while循环的次数!总共有n个元素,渐渐跟下去就是n,n/2,n/4,....n/2^k,其中k就是循环的次数由于你n/2^k取整后>=1,即令n/2^k=1

可得k=log2n,(是以2为底,n的对数)所以时间复杂度可以表示O()=O(logn);


所以我们这个题目要向二分查找上想,参考leetcode,整体的思路如下:

1)我们将数组A剪成两部分

                          left_A             |        right_A
               A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]

因为A有m个元素,因此有m+1中分段可能,我们知道 len(left_A) = i, len(right_A)= m - i . 当i = 0 , left_A为空, 当 i = m , right_A为空。.

2)同样我们对B也做该处理

                     left_B             |        right_B
           B[0], B[1], .., B[j-1]  |  B[j], B[j+1], ..., B[n-1]

3)将left_A  left_B 看成一个数组,同时将right_A  right_B 当成另一个.我们分别命名为left_part何right_part.

                    left_part          |        right_part
         A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]
          B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1]

4)如果我们能确认

               1) len(left_part) == len(right_part)
               2) max(left_part) <= min(right_part)

那么我们将所有在{A,B}中的元素,分成两个相等的部分,左边的部分的元素全部小于右边部分的元素,中间值为median= (max(left_part) + min(right_part))/2.0

5)为了确保这两个情况,我们需要确定

   (1) 2(i + j)==m+n或者2(i + j)=m+n+1整理如下:
    i + j == m - i + n - j (or: m - i + n - j + 1)
    if n >= m, 我们设置i = 0 ~ m, j = (m + n + 1)/2 – i
   (2) B[j-1] <= A[i] and A[i-1] <= B[j]

在满足(1)的情况下满足(2)则i被找到,当i被找到返回值情况见7);

PS:1. 我假设 A[i-1],B[j-1],A[i],B[j] 总是有效的,即使 i=0,i=m,j=0,j=n ,我将会讨论如何处理这些边界问题。

PS:2.  为什么假设n>=m,是因为必须确认j是一个正数,由于0 <= i<= mj = (m + n + 1)/2 - i. 如果n<m,  j可能为负数,,会导致结果错误。

         所以寻找i必须在0~m之间寻找,i满足 j = (m + n + 1)/2 - i 时,B[j-1] <= A[i] 和 A[i-1] <= B[j],

6)在这面我们可以做一个简单的程序流程“口述”

1、设置 imin=0imax=m

2、设置i = (imin + imax)/2,j = (m + n + 1)/2 – i//ij的索引分别是数组AB的中间索引+1

3、现在我们有len(left_part)==len(right_part),这里可能出现三种情况;

<a>B[j-1] <= A[i]and A[i-1] <= B[j]//这意味着我们找到了i停止搜索

<b> B[j-1] > A[i]//说明A[i]较小,我们必须调整i使得:B[j-1] <= A[i]
如何调整i的值:
PS1:能增加i的值吗?可以,因为增加i的值,这时j将减小,i+j的和不变,这时B[j-1] 是递减,A[i]是递增,这样可能会满足B[j-1] <= A[i],这时找到i
PS2:能减小i的值吗?显然不可能,因为A[i]已经足够小了,i减小,j增大,这时B[j-1]是递增,A[i]是递减,不可能满足B[j-1] <= A[i]。这种情况在程序中舍弃
小结:因此只能递增imin = i+1, 然后执行2;

<c> A[i-1] > B[j]
       说明A[i-1]是大的.必须递减i的值,才会满足A[i-1]<=B[j],这样我们调整i的范围 [imin, i-1]。因此设置 imax = i-1,,然后接着执行2
 

7)当i被找到时,中间值为:

m+n是奇数时: max(A[i-1], B[j-1])
m+n是偶数时: (max(A[i-1], B[j-1]) + min(A[i], B[j]))/2
 

8)现在我们考虑边界情况, i=0,i=m,j=0,j=n  A[i-1],B[j-1],A[i],B[j] 不存在. 我们需要确保的是max(left_part) <= min(right_part),因此如果i和j不是边界值, A[i-1],B[j-1],A[i],B[j] 存在,我们必须检查 B[j-1] <= A[i]A[i-1] <= B[j]情况,但是如果 A[i-1],B[j-1],A[i],B[j] 不存在,则不需要检查 B[j-1] <= A[i]A[i-1] <= B[j]。例如,如果j=0 A[i-1] 不存在, 我们不需要检查 A[i-1] <= B[j]。因此我们需要做的是: [0, m]寻找i,当j = (m + n + 1)/2 – i

    (j ==0 or i == m or B[j-1] <= A[i])以及 (i == 0or j == n or A[i-1]<= B[j])

9)在寻找i的时候发生跳转只会发生下面三种情况:

    <a> (j == 0 ori == m or B[j-1] <= A[i]) (i == 0 or j = n or A[i-1]<= B[j])

          说明我们找到了i,停止搜索

    <b> j > 0 i< m B[j- 1] > A[i]

          说明i较小,这时增加i

    <c> i > 0 andj < n and A[i - 1] > B[j]

          说明i较大递减i

PS:因为 i < m ==> j > 0 and i > 0 ==> j < n .证明如下:

         m <= n, i < m ==> j = (m+n+1)/2 - i > (m+n+1)/2 - m >= (2*m+1)/2 - m >= 0    
         m <= n, i > 0 ==> j = (m+n+1)/2 - i < (m+n+1)/2 <= (2*n+1)/2 <= n

         因此在<b> <c>中,我们不需要考虑,是否j > 0 j < n.

综上自己修正的代码如下:

class Solution1 {
public:
	double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2){
		int m = nums1.size(), n = nums2.size(), max_of_left = 0, min_of_right = 0;
		if (m > n) return findMedianSortedArrays(nums2, nums1);
		int i, j, imin = 0, imax = m, half = (m + n + 1) / 2;
		while (imin <= imax)
		{
			i = (imin + imax) / 2, j = half - i;
			if (i > 0 && nums1[i - 1] > nums2[j])
				imax = i - 1; //i太大,应该递减
			else if (j > 0 && nums2[j - 1] > nums1[i])
				imin = i + 1; //i太小,应该递增
			else
				break;
		}
		//如果i找到后
		if (i == 0) max_of_left = nums2[j - 1];
			else if (j == 0) max_of_left = nums1[i - 1];
				else max_of_left = max(nums1[i - 1], nums2[j - 1]);
		if ((m + n) % 2)
			return max_of_left;
		if (i == m) min_of_right = nums2[j];
			else if (j == n) min_of_right = nums1[i];
				else min_of_right = min(nums1[i], nums2[j]);
		return (max_of_left + min_of_right) / 2.0;
	}
};
如有错误,请大家指正。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值