LeetCode Problem:两个有序数组的中位数

Median of Two Sorted Arrays

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)).

这个题目是非常有名的面试题,

注意leetcode上面对中位数的定义是:

如果合并之后的数组元素个数是奇数,那么中位数就是中间的那位

如果合并之后的数组元素个数是奇数,那么中位数是中间两位的平均数

解题思路有三种

解法一:

使用合并排序,将两个数组重新排序成一个新的数组,这样在O(1)的时间可以找到中位数,排序的时间是O(n+m)

 

解法二:

先假设数组A的长度为m,数组B的长度为n

1. 先分别找出数组A和数组B的中位数 ma 和 mb,设A[i] = ma , B[j] = mb 然后比较大小

2. 如果 ma < mb, 即A[i] < B[j], 那么中位数一定在A[i..m] 和 B[1..j] 之间, 然后再这个区间再求中位数,递归查找

3. 如果 ma > mb, 即A[i] > B[j],那么中位数一定在A[1..i] 和 B[j..n] 之间,然后再这个区间再求中位数,递归查找

 

算法比较好理解,但是程序有两点需要注意:

1) 递归的base条件是,当其中一个数组元素个数只剩下小于等于2时,递归结束

2) 每次递归时,在两个数组中选择除去的元素一定要相等。比如说A数组减去两个元素,那么B数组也要减去两个,这样剩下的区间的中位数和原来的中位数是一样的

 

程序如下: 这个代码过了leetcode的所有case,算是bug free了。

/**
	 * find median of two sorted array, the values in array may same
	 * 
	 * <p>
	 * The algorithm are:
	 * </p>
	 * <ol>
	 * <li>Calculate the medians m1 and m2 of the input arrays A and B
	 * respectively.</li>
	 * <li>If m1 is less than m2, then the median located between
	 * A[m-index(m1)..m] and B[0..index(m2)], index(m1),index(m2) are the index
	 * of array A and B,repeat finding median recrusively</li>
	 * <li>If m1 is greater than m2, then the median located between
	 * A[0..index(m1)] and B[n-index(m2)..n] ,repeat finding median recrusively</li>
	 * </ol>
	 * 
	 */
	static class FindMedian1 {
		public static double medianSelect(int[] A, int[] B) {
			if (A == null && B == null) {
				return -1;
			} else if ((A == null || A.length == 0) && B.length != 0) {
				return B.length % 2 == 1 ? B[(B.length - 1) / 2]
						: (B[(B.length - 1) / 2] + B[(B.length - 1) / 2 + 1]) / 2.0;
			} else if (B == null || B.length == 0 && A.length != 0) {
				return A.length % 2 == 1 ? A[(A.length - 1) / 2]
						: (A[(A.length - 1) / 2] + A[(A.length - 1) / 2 + 1]) / 2.0;
			}
			return findMedian(A, 0, A.length - 1, B, 0, B.length - 1);
		}

		public static double findMedian(int[] A, int p, int r, int[] B, int l,
				int h) {
			if (p == r) {// only one element left to compare
				return findMedianBaseCase1(A[p], B, l, h);
			} else if (l == h) {
				return findMedianBaseCase1(B[l], A, p, r);
			} else if (r == p + 1) {
				return findMedianBaseCase2(A[p], A[r], B, l, h);
			} else if (h == l + 1) {
				return findMedianBaseCase2(B[l], B[h], A, p, r);
			}
			int medA = (p + r) / 2;
			int medB = (l + h) / 2;
			if (A[medA] <= B[medB]) {
				int k = 0;
				if ((r - p + 1) % 2 == 0) {
					k = Math.min(medA - 1, h - medB - 1);
				} else {
					k = Math.min(medA, h - medB - 1);
				}
				k = k == 0 ? 1 : k;
				return findMedian(A, p + k, r, B, l, h - k);
			} else {
				int k = 0;
				if ((h - l + 1) % 2 == 0) {
					k = Math.min(medB - 1, r - medA - 1);
				} else {
					k = Math.min(medB, r - medA - 1);
				}
				k = k == 0 ? 1 : k;
				return findMedian(A, p, r - k, B, l + k, h);
			}
		}

		public static double findMedianBaseCase1(int med, int[] A, int p, int r) {
			if (r == p) {
				return (med + A[p]) / 2.0;
			}

			if (med == A[(r + p) / 2]) {
				return med;
			} else if (med < A[(r + p) / 2]) {
				if ((r - p + 1) % 2 == 0) {
					return A[(r + p) / 2];
				} else {
					return (Math.max(med, A[(r + p) / 2 - 1]) + A[(r + p) / 2]) / 2.0;
				}
			} else {// if(med > A[(r+p)/2])
				if ((r - p + 1) % 2 == 1) {
					return (A[(r + p) / 2] + Math.min(med, A[(r + p) / 2 + 1])) / 2.0;
				} else {
					return med < A[(r + p) / 2 + 1] ? med : A[(r + p) / 2 + 1];
				}
			}
		}

		// med2 > med1 by default
		public static double findMedianBaseCase2(int med1, int med2, int[] A,
				int p, int r) {
			int medA = (r + p) / 2;
			if (r == p + 1) {
				if (med1 > A[p]) {
					if (med2 < A[r]) {
						return (med1 + med2) / 2.0;
					} else {
						return (med1 + A[r]) / 2.0;
					}
				} else if (med2 > A[r]) {
					return (A[r] + A[p]) / 2.0;
				} else {
					return (med2 + A[p]) / 2.0;
				}
			}
			if ((r - p + 1) % 2 == 0) {
				if (med2 < A[medA]) {
					return (Math.max(med2, A[medA - 1]) + A[medA]) / 2.0;
				} else if (med1 > A[medA]) {
					if (med2 < A[medA + 1]) {
						return (med1 + med2) / 2.0;
					} else {// med2 > A[medA+1]
						return (Math.min(med1, A[medA + 2]) + A[medA + 1]) / 2.0;
					}
				} else {
					return (A[medA] + Math.min(med2, A[medA + 1])) / 2.0;
				}
			} else {
				if (med1 > A[medA]) {
					return Math.min(A[medA + 1], med1);
				} else if (med2 < A[medA]) {
					return Math.max(med2, A[medA - 1]);
				} else {
					return A[medA];
				}
			}
		}
	}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值