4 寻找两个正序数组的中位数Median of Two Sorted Arrays算法优化


蓝雨原创,转载请注明出处。contact author: qinglanyu_jun@foxmail.com

There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the meidan of the two sorted arrays. The overall run time complexity should O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n)).
You may assume nums1 and nums2 cannot be both empty.

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

Solution 1 合并选择

最容易想到的办法,将两个有序数组进行合并,取最中间的数(合并后奇数个元素)或者最中间两个数(合并后偶数个元素)的平均数即可。其中合并后元素的个数是可以计算的,因此只需要找出合并后的中间元素即可,无需开辟空间存储合并后的数组,最多两个元素的空间用来存储遍历到的最后两个元素。

Code
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        unsigned m = nums1.size(), n = nums2.size();
        unsigned last = (m+n)/2+1;
        unsigned i=0, j=0;
        int meidans[2] = {0};
        unsigned idx = 0;

        while(idx < last)
        {
            if(i<m && j<n)
            {
                meidans[idx&1] = (nums1[i]<=nums2[j]) ? (nums1[i++]) : (nums2[j++]);
            }
            else if(i<m)
            {
                meidans[idx&1] = (nums1[i++]);
            }
            else if(j<n)
            {
                meidans[idx&1] = (nums2[j++]);
            }
            idx ++;
        }

        return (double)(((m+n)&1) ? (meidans[(idx+1)&1]) : ((meidans[0]+meidans[1])/2.0));
    }
};

执行结果:通过
执行用时:16ms,在所有C++提交中击败了84.66%的用户
内存消耗:7.3MB,在所有C++提交中击败了100.00%的用户

然而以上方法的时间复杂度为 O ( m + n ) O(m+n) O(m+n),达不到 O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n))的要求。

Solution 2 切分数组

设两有序数组分别为 A = { A [ 0 ] , A [ 1 ] , . . . , A [ m ] } A = \{A[0], A[1], ..., A[m]\} A={A[0],A[1],...,A[m]} B = { B [ 0 ] , B [ 1 ] , . . . , B [ n ] } B = \{B[0], B[1], ..., B[n]\} B={B[0],B[1],...,B[n]},现将 A A A B B B都拆分为左右两部分即:

A = A l e f t + A r i g h t A = A_{left} + A_{right} A=Aleft+Aright,
A l e f t = { A [ 0 ] , A [ 1 ] , . . . , A [ i − 1 ] } A_{left} = \{A[0], A[1], ..., A[i-1]\} Aleft={A[0],A[1],...,A[i1]}, A r i g h t = { A [ i ] , A [ i + 1 ] , . . . , A [ m ] } A_{right} = \{A[i], A[i+1], ..., A[m]\} Aright={A[i],A[i+1],...,A[m]}

B = B l e f t + B r i g h t B = B_{left} + B_{right} B=Bleft+Bright,
B l e f t = { B [ 0 ] , B [ 1 ] , . . . , B [ j − 1 ] } B_{left} = \{B[0], B[1], ..., B[j-1]\} Bleft={B[0],B[1],...,B[j1]}, B r i g h t = { B [ j ] , B [ j + 1 ] , . . . , B [ n ] } B_{right} = \{B[j], B[j+1], ..., B[n]\} Bright={B[j],B[j+1],...,B[n]}

此时,令
L = A l e f t + B l e f t L = A_{left} + B_{left} L=Aleft+Bleft
R = A r g i h t + B r i g h t R = A_{rgiht} + B_{right} R=Argiht+Bright

m + n m+n m+n A A A B B B的总长度为偶数时,条件
s i z e ( L ) = s i z e ( R ) size(L) =size(R) size(L)=size(R)
m a x ( L ) ≤ m i n ( R ) max(L) \leq min(R) max(L)min(R)
满足,那么所寻找的中位数为:
m e d i a n = m a x ( L ) + m i n ( R ) 2 median = \frac{max(L)+min(R)}{2} median=2max(L)+min(R)

m + n m+n m+n A A A B B B的总长度为奇数时,条件
s i z e ( L ) = s i z e ( R ) + 1 size(L) =size(R)+1 size(L)=size(R)+1
m a x ( L ) ≤ m i n ( R ) max(L) \leq min(R) max(L)min(R)
满足,那么所寻找的中位数为:
m e d i a n = m a x ( L ) median = max(L) median=max(L)

又由于上述两组条件等价于以下三个条件:
条件一:
i + j = ( m − i ) + ( n − j ) ⇒ i + j = ⌊ m + n + 1 2 ⌋ i+j=(m-i) + (n-j) \Rightarrow i+j=\lfloor\frac{m+n+1}{2}\rfloor i+j=(mi)+(nj)i+j=2m+n+1 ,当 m + n m+n m+n为偶数时
i + j = ( m − i ) + ( n − j ) + 1 ⇒ i + j = ⌊ m + n + 1 2 ⌋ i+j=(m-i) + (n-j) + 1 \Rightarrow i+j=\lfloor\frac{m+n+1}{2}\rfloor i+j=(mi)+(nj)+1i+j=2m+n+1,当 m + n m+n m+n为奇数时

条件二:
假设 m ≤ n m \leq n mn,且 0 ≤ i ≤ m , 0 ≤ j ≤ n 0 \leq i \leq m, 0\leq j\leq n 0im,0jn,
则对于 ∀ i ∈ [ 0 , m ] \forall i \in[0, m] i[0,m],都有 j = ( ⌊ m + n + 1 2 ⌋ − i ) ∈ [ 0 , n ] j=(\lfloor\frac{m+n+1}{2}\rfloor-i)\in[0,n] j=(2m+n+1i)[0,n]

条件三:
B [ j − 1 ] ≤ A [ i ] B[j-1]\leq A[i] B[j1]A[i]
A [ i − 1 ] ≤ B [ j ] A[i-1]\leq B[j] A[i1]B[j]

为了简化计算,假设 A [ i − 1 ] , B [ j − 1 ] , A [ i ] , B [ j ] A[i-1], B[j-1], A[i], B[j] A[i1],B[j1],A[i],B[j]总是存在 。对于 i = 0 , i = m , j = 0 , j = n i=0, i=m,j=0,j=n i=0,i=m,j=0,j=n这样的临界条件,只需规定 A [ − 1 ] = B [ − 1 ] = − ∞ A[-1]=B[-1]=-\infty A[1]=B[1]= A [ m ] = B [ n ] = ∞ A[m]=B[n]=\infty A[m]=B[n]=即可。 因为其计算并不会影响 L L L R R R中的最大值或最小值产生影响。

最后程序需要计算的是:

在[0,m]中找到i,使得:
     B[j-1]<=A[i]且A[i-1]<=B[j],其中j=(m+n+1)/2-i

同时上述条件也等价于

在[0,m]中找到最大的i,使得:
     A[i-1]<=B[j],其中j=(m+n+1)2-i

因为:
i i i 0 ∼ m 0\sim m 0m 递增时, A [ i − 1 ] A[i-1] A[i1]递增, B [ j ] B[j] B[j]递减,所以一定存在一个最大的 i i i满足 A [ i − 1 ] ≤ B [ j ] A[i-1]\leq B[j] A[i1]B[j];
而对于最大的 i i i,有 i + 1 i+1 i+1不满足,即代入 i + 1 i+1 i+1则有 A [ i ] > B [ j − 1 ] A[i]>B[j-1] A[i]>B[j1],也就是 B [ j − 1 ] < A [ i ] B[j-1]<A[i] B[j1]<A[i]

综上所述,在区间 [ 0 , m ] [0,m] [0,m]上对 i i i进行二分查找 ,找到最大的满足 A [ i − 1 ] ≤ B [ j ] A[i-1]\leq B[j] A[i1]B[j] i i i值,得到了正确的划分有序数组 A A A B B B为有序数组左 L L L R R R两部分的方法。

Code
class Solution {
#define MAX(a, b) ((a)>(b)?(a):(b))
#define MIN(a, b) ((a)<(b)?(a):(b))
public:
	double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
		if (nums1.size() > nums2.size()){
			return findMedianSortedArrays2(nums2, nums1);
		}

		unsigned m = nums1.size(), n = nums2.size();
		int left = 0, right = m;
		int maxL = 0, minR = 0;

		while (left <= right){
			// L = nums1[0, .., i-1], nums2[0, .., j-1]
			// R = nums1[i, .., m-1], nums2[j, .., n-1]
			int i = (left + right) / 2;
			int j = (m + n + 1) / 2 - i;

			int nums_i_1 = (i == 0 ? INT_MIN : nums1[i - 1]);	// nums1[i-1]
			int nums_i = (i == m ? INT_MAX : nums1[i]);			// nums1[i]
			int nums_j_1 = (j == 0 ? INT_MIN : nums2[j - 1]);	// nums2[j-1]
			int nums_j = (j == n ? INT_MAX : nums2[j]);			// nums2[j]

			if (nums_i_1 <= nums_j){
				maxL = MAX(nums_i_1, nums_j_1);
				minR = MIN(nums_i, nums_j);
				left = i + 1;
			}
			else{
				right = i - 1;
			}
		}

		return ((m + n) & 1) == 0 ? (maxL + minR) / 2.0 : maxL;
	}

执行结果:通过
执行用时:16ms,在所有C++提交中击败了84.42%的用户
内存消耗:7.1MB,在所有C++提交中击败了68.98%的用户

以上方法的时间复杂度为 O ( l o g m i n ( m , n ) ) O(log min(m,n)) O(logmin(m,n)),其中 m m m n n n另是数组 n u m s 1 nums1 nums1 n u m s 2 nums2 nums2的长度。
空间复杂度为 O ( 1 ) O(1) O(1)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

轻蓝雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值