现在的位置: 首页 > 综合 > 正文

Median of two sorted arrays

2017年12月23日 ⁄ 综合 ⁄ 共 3246字 ⁄ 字号 评论关闭

 

转自:http://blog.unieagle.net/2012/10/04/leetcode%E9%A2%98%E7%9B%AE%EF%BC%9Amedian-of-two-sorted-arrays/

 

根据文章中的算法,每次能够剔除掉 k/2 个元素,每次减小一半,而 k= (m+n)/2 ,所以最后的复杂度是 log k = log(m+n),符合题意。

 

这个问题可以用二分来解,算法有点复杂。

    先说临界情况

  1. A为空或者B为空
    直接在非空数组中找第k大的数即可。O(1)
  2. 找最小的数,k==0的情况,也简单,比较两个数组最开头的元素,谁小就是谁
    然后就是比较复杂的情况,假设寻找目标target是下标为k的数。
    那么意味着在排好的数组中,在目标数之前,一共有k个比目标更小的数。
    将k分成两份,一份在A的前端,一份在B的前端。这里其实将k怎么分配是一个可以讨论的问题,但是平分k可以得到平均最快的效果。
    设k = ka + kb,(k是偶数简单,k是奇数的话,剩下那一个随便放在两个数组中哪一个中都可以)

    这里可以列出来我们想要的效果:
    k=1 —-> ka = 1, kb = 1
    k=2 —-> ka = 1, kb = 1
    k=3 —-> ka = 1, kb = 1. [+1,表示还有一个元素,可以随意分配在ka或者kb中,只要不越界]
    k=4 —-> ka = 2, kb = 2
    k=5 —-> ka = 2, kb = 2. [+1]
    已经可以看出来规律了,这个造成了下面代码中比较复杂的部分,这些细节消耗的时间不少啊。

    然后就是主要逻辑

  1. 如果A[ka-1] >= B[kb-1]
    说明在B前端的kb个数中,不可能出现我们要寻找的目标。
    为什么呢?
    假如A一共有m个数,B一共有n个数。
    那么target(下标是k)后面有且只有n + m – 1 – k个数;
    但是B[kb-1]在B中已经排在n – kb个数之前,加上A中的m – ka + 1(A[ka-a]),也就是说在排序后的整体数组中排在B[kb-1]之后的数,至少有n – kb + m – ka + 1 = n + m – k + 1个。
    由于n + m – k + 1 > n + m – k – 1,所以B前端kb个数都不可能是target。
    所以此时可以将问题转化为,在A[0,...,m-1]和B[kb,...,n-1]中,寻找下标为k – kb的数。
  2. 否则,A[ka-1] < B[kb-1]
    同上,可以剔除A前端的ka个数。

这样循环下去,就能以二分的速度找到目标。

这个问题不仅要找到第k大的数,当C是偶数的时候,还要找到第k+1个数,取两者均值。

 

2.第二个方法是类似于找第Kth个数:注意没有getV方法会报错。

注意换数组查找的上下界确定,下界表示即使另一数组全排在前面,上界是下标自然的限制;

 

double findMedian(int a[],int b[],int l,int r,int m,int n)
	{
		if ( l > r) return findMedian(b,a,max(0,(m+n)/2-m),min(n,(m+n)/2),n,m);
		int i= (l+r)/2;
		int j=(m+n)/2-i-1;
		if ( getV(a,m,i)< getV(b,n,j) )
			return findMedian(a,b,i+1,r,m,n);
		else if ( getV(a,m,i)> getV(b,n,j+1))
			return findMedian(a,b,l,i-1,m,n);
		else
		{
			if( (m+n)&1 ==1 )
				return getV(a,m,i);
			else 
				return (getV(a,m,i)+max(getV(b,n,j),getV(a,m,i-1)))*0.5;
		}
	}

	double findMedianSortedArrays(int a[],int m,int b[],int n)
	{
		if ( m < n ) 
			return findMedian(a,b,0,m-1,m,n);
		else
			return findMedian(b,a,0,n-1,n,m);
	}
	int getV(int a[],int m,int i)
	{
		if (i<0) return -10000000;
		if (i>=m) return 10000000;
		else return a[i];
	}

3.第三种方法是二分 D&C, 去A的中位数和B的中位数,如果A[i]<=B[j],那么应带在A[i]右边和B[j]左边寻找,理由是A[i]最大的序号也小于最后的median,B[j]的序号

最小也不会超过median。重点在于首先A[i[和B[j]不要去除,同时取出的长度应当相等。另外麻烦的地方在一些边界条件的处理。还有,要保证A总是短于B。

原文:http://ideone.com/FtqjM

double MedianOfFour (int a, int b, int c, int d)
{
        int minValue = min (d, min (a, min (b,c) ) );
        int maxValue = max (d, max (a, max (b,c) ) );
        return (a + b + c + d - minValue - maxValue) / 2.0 ;
}
 
double MedianOfThree (int a, int b, int c)
{
        int minValue = min (a, min (b,c) ) ;
        int maxValue = max (a, max (b,c) ) ;
        return (a + b + c - minValue - maxValue);
}
 
//constraint : n <= m
double MedianSortedArrays (int A[MAX], int n, int B[MAX], int m)
{
        //base case # 1
        if ( n == 1 ) 
        {
                if ( m == 1 ) 
                        return (A[0] + B[0]) / 2.0; 
                if ( m % 2 == 1) 
                         return ( B[m/2] + MedianOfThree (A[0], B[m/2-1], B[m/2+1]) ) / 2.0 ;
                else 
                        return MedianOfThree ( A[0], B[m/2-1], B[m/2] );
        }
 
        //base case # 2
        if ( n == 2 ) 
        {
                if ( m == 2 )
                        return MedianOfFour (A[0], A[1], B[0], B[1]);
                if ( m % 2 == 1 )
                        return MedianOfThree ( B[m/2], min(A[0], B[m/2+1]), max (A[1], B[m/2-1]) ) ;
                else 
                        return MedianOfFour ( B[m/2-1], B[m/2], min(A[0], B[m/2+1]), max(A[1], B[m/2-2]) );
        }
 
 
        int minRemoved, idxA = n/2 , idxB = m/2 ;
 
        if ( A[idxA] < B[idxB]  )                                               
        {
                if ( n % 2 == 0 ) --idxA;       //for even number of elements --idxA points to lower median of A[]
                minRemoved = min ( idxA, m - idxB - 1) ;        
                return MedianSortedArrays ( A + minRemoved, n - minRemoved, B, m - minRemoved); 
        }
        else                                                                                    
        {
                if ( m % 2 == 0 ) --idxB;       //for even number of elements --idxB points to lower median of B[]
                minRemoved = min ( n - idxA - 1, idxB) ;        
                return MedianSortedArrays ( A, n - minRemoved, B + minRemoved, m - minRemoved); 
        }
 
}

 

三种方法中第二种明显较简洁,但缺点是在一个数组中查找,如果失败,在另一个中查找,所以效率可能稍低。

我的思路与方法二类似:在数组A中取中位数A[i],然后在B中二分查找A[i],这样可以确定A[i]排在median前还是后,从而缩小区间,但缺点是查找到A[i]后要log(Blen)才可以确定下一区间,而第二种方法只要在常数时间就可以确定下一区间,复杂度低了一个级别。

 这里有个更好的总结:http://www.geeksforgeeks.org/median-of-two-sorted-arrays/

抱歉!评论已关闭.