[leetcode]Median of Two Sorted Arrays

时间:2022-02-27 12:17:33

There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

1. 第一次看到此题时,我的想法是现在A中折半查找缩小范围,比如取得A的中间数A[i],然后在B中找到A[i],然后便可以缩小范围。A中没有再找B。这个与最后的推荐做法有些类似。
2. 错误或当时没想清楚的是,在B中找A[i]需要log(n)的时间,但其实不需要找A[i],直接找B[(m+n)/2 -i]作比较就行,是O[1]的时间,这样,整体就是O(log(n+m))。 此方法是在此处有描述:http://www2.myoops.org/course_material/mit/NR/rdonlyres/Electrical-Engineering-and-Computer-Science/6-046JFall-2005/30C68118-E436-4FE3-8C79-6BAFBB07D935/0/ps9sol.pdf
3. 另外的方法是:http://leetcode.com/2011/03/median-of-two-sorted-arrays.html 这篇文章提到的方法自己也承认,太复杂太难写了。其思想是先找A的中位数和B的中位数比大小,然后可得到两个数组都可舍弃一半,但舍弃的时候两个数组要舍弃同样数目的数字,这样才能继续实施算法时,新数组们的中位数是原两数组的中位数。
4. 一开始以为要对第一个和第二个中位数分别查找(n+m为偶数时),后来发现只要找到第一个中位数,第二个要么出现在同数组右边,要么在对应数组固定位置。
5. 使用推荐的做法编程时,发现是个很难写对的程序,需要考虑很多情况,同时经常有小错误。
6. 发现个小办法有点用:从最小的数组开始,然后慢慢变大。比如{1} {2,3} => {1,4} {2,3},这样能用较少的次数在纸上覆盖较多的情况。
7. 先多列出一些test case。无论面试还是写程序,都会有好处。

class Result
{
    public int[] array;
	public int index;
}

public class Solution {
    public double findMedianSortedArrays(int A[], int B[]) {
        // Start typing your Java solution below
        // DO NOT write main() function
    	
    	// special cases
    	if (A.length == 0){
    		if (B.length % 2 == 0)
    		{
    			return (B[B.length / 2 - 1] + B[B.length / 2]) / 2.0;
    		}
    		else
    		{
    			return B[B.length / 2];
    		}
    	}
    	else if (B.length == 0)
    	{
    		if (A.length % 2 == 0)
    		{
    			return (A[A.length / 2 - 1] + A[A.length / 2]) / 2.0;
    		}
    		else
    		{
    			return A[A.length / 2];
    		}
    	}
    	else if (A.length == 1 && B.length == 1)
    	{
    		return (A[0] + B[0]) / 2.0;
    	}

    	Result r = findMedFromFirstArray(A, B);
    	if (r.index == Integer.MIN_VALUE){
    		r = findMedFromFirstArray(B, A);
    	}
    	if (( A.length + B.length) % 2 == 0) // need to find med2
    	{
    		int med2 = findMed2(r, A, B);
    		return (r.array[r.index] + med2) / 2.0;
    	}
    	else
    	{
    		return r.array[r.index];
    	}

    }

    private Result findMedFromFirstArray(int A[], int B[])
    {
    	int la = 0;
    	int ra = A.length - 1;
    	int total = 0;
    	if (( A.length + B.length) % 2 == 0)
    	{
    		total = (A.length + B.length)/2 - 2;
    	}
    	else 
    	{
    		total = (A.length + B.length)/2 - 1;
    	}
    	while (la <= ra)
    	{
    		int mid = (la + ra) / 2;
    		int idx =  total - mid;

            if ((idx < 0 && (idx + 1) >= 0 && A[mid] <= B[idx + 1]) ||
            		(idx >= 0 && idx < B.length && idx + 1 >= B.length && B[idx] <= A[mid]) ||
    		    	(idx >= 0 && idx + 1 < B.length && B[idx] <= A[mid] && A[mid] <= B[idx + 1]))
    		{
    				Result r = new Result();
    				r.index = mid;
    				r.array = A;
    				return r;
    		}
			if (idx >= B.length)
			{
				la = mid+1;
			}
			else if (idx < 0)
			{
				ra = mid-1;
			}
			else if (B[idx] > A[mid])
    		{
    			la = mid + 1;
    		}
			else
			{
				ra = mid - 1;
			}
    	}
    	Result r = new Result();
		r.index = Integer.MIN_VALUE; // this means not find
		r.array = A;
		return r;
    }
    
    private int findMed2(Result r, int ArrayA[], int ArrayB[])
    {
    	int target1 = Integer.MAX_VALUE;
		int target2 = Integer.MAX_VALUE;
		int[] A = ArrayA;
		int[] B = ArrayB;
		if (r.array == B)
		{
			A = ArrayB;
			B = ArrayA;
		}
		{
			if (r.index + 1 < A.length)
			{
				target1 = A[r.index + 1];
			}
			int idx = ( A.length + B.length) / 2 - 2 - r.index + 1;
			if (idx >= 0 && idx < B.length)
			{
				target2 = B[idx];
			}
			
			if (target1 < target2)
			{
				return target1;
			}
			else
			{
				return target2;
			}
		}
    }
    	
}

Discuss上有这段代码,理解一下,基本上是在A中折半猜是不是中位数,A中没有后在B中猜。最后猜到B[j]<=A[i]<=B[j+1],此时,无论奇偶(2k+1或者2k个),A[i]都是第k+1那个。那么奇数时,A[i]是正中的那个;偶数时,A[i]是中位数两个里大的那个,小的那个要从B[j]和A[i-1]里选一个。

double findMedian(int A[], int B[], int l, int r, int nA, int nB) {
	if (l > r) 
		return findMedian(B, A, max(0, (nA+nB)/2-nA), min(nB, (nA+nB)/2), nB, nA);
	int i = (l+r)/2;
	int j = (nA+nB)/2 – i – 1;
	if (j ≥ 0 && A[i] < B[j]) return findMedian(A, B, i+1, r, nA, nB);
	else if (j < nB-1 && A[i] > B[j+1]) return findMedian(A, B, l, i-1, nA, nB);
	else {
		if ( (nA+nB)%2 == 1 ) return A[i];
		else if (i > 0) return (A[i]+max(B[j], A[i-1]))/2.0;
		else return (A[i]+B[j])/2.0;
	}
}