两个有序数组的中位数问题

两个有序数组的中位数问题

———leetcode 第4题

来自leetcode上的答案的整理。

Description:

Median of Two Sorted Arrays

There are two sorted arrays nums1 and nums2 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)).

You may assume nums1 and nums2 cannot be both empty.

描述:有两个非降有序数组,大小分别是m,n,找这两个数组的中位数。假设这两个数组不能全为空。

a) 暴力破解方式

开辟一个新数组,大小为m+n,用归并的方式将两个数组重新排序的一个数组内,然后找中位数,代码比较容易。

b) 不断减少问题规模得到解

问题抽象:

对于两个有序数组A,B,要找集合{A, B}中kth的数,这里找的是中位数( k=(m+n+1)/2 or k=( (m+n+1)/2+(m+n+2)/2 )/2 ).

A的长度为m,B的长度为n,确保m<=n(如果不满足),首先我们分别找这两个数组的k/2位置的元素,我们将会遇到两种情况:

  1. A[k/2]>B[k/2],因为是有序的,那么中位数不会位于B[k/2]之前的部分,变成了一个问题规模更小的子问题。

    B[0], B[1], …, B[k/2] | B[k/2+1], B[k/2+2], …, B[n-1]

    这时候A没有约减,考虑到我们去掉B[0]~B[k/2],去掉了k/2个最小的数了,我们要在新的数组内找(k-k/2)大的数。

    然后不停递归比较约减下去。

  2. A[k/2]<=B[k/2],因为是有序的,那么中位数不会位于A[k/2]之前的部分,问题规模得以简化。

    A[0], A[1], …, A[k/2] | A[k/2+1], A[k/2+2], …, A[m-1]

这时候B没有约减,同样也是去掉了k/2个最小的数了,我们要在新的数组内找(k=k-k/2)大的数。

然后不停递归比较约减下去。

当最后k-k/2=1的时候,表示我们找到了我们要找的那个数。

伪代码:

function findMedianSortedArrays(A,B):
	m=A.size;
	n=B.size;
	left=(m+n+1)>>1;
	right=(m+n+2)>>1;
	return (getkth(A,m,B,n,l)+getkth(A,m,B,n,r))/2.0;
end function;
function getkth(s[],m,l[],n,k):
	if m>n then:
		return getkth(l,n,s,m,k);
	end if;
	if m==0 then:
		//一个数组全部约减掉,等于在另一个数组找第k大的数。
        return l[k-1]
	end if;
	if k==1 then:
		//找最小的数,表示我们约减掉的已经足够多了
		//只需要比较约减后的两个数组的首元素哪个最小
		return min(s[0], l[0]);
	end if;
	//决定约减哪个数组
	int i=min(m,k/2),j=min(n,k/2);
	if s[i-1]>l[j-1] then:
		return getkth(s,m, l+j,n-j,k-j);
	else
		return getkth(s+i,m-i,l,n,k-i);
	return 0;
end function;
正确性分析:

正确性内含在伪代码注释和问题分析中的。

时间复杂度分析:

子问题简化式子: T ( n ) = T ( 3 n 4 ) + k , k 是 个 常 数 T(n)=T(\frac{3n}{4})+k,k是个常数 T(n)=T(43n)+k,k,这里的n=m+n,原问题的规模大小是两个数组长度。

故时间复杂度为: O ( log ⁡ ( m + n ) ) O(\log{(m+n)}) O(log(m+n))

c) 基于中位数的二分查找

首先理解中位数的用处。

从统计上,中位数被用于:将一个集合划分为两个等长的子集,其中一个子集的元素都大于另一个子集。

对数组A,在i位置切分成两部分,对数组B,在j位置切成两部分,

把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]

同时必须确保这两个集合的长度相等,一个集合内的元素总是大于另一个集合内的元素,即:

  1. len(left_part)=len(right_part)
  2. max(left_part)≤min(right_part)

然后,我们就可以把{A, B}内所有元素划分成两个子集,其中一个子集内的所有元素总是大于另一个子集。然后;
m e d i a n = m a x ( l e f t _ p a r t ) + m i n ( r i g h t _ p a r t )

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值