求两个有序数组的中位数

要求: 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
例如:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0

nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5

算法解析:

中位数:将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
1. 两个有序的数组分别为:A和B。其中A长度为m,B长度为n
我们把A分成两部分:
      left(A)            |        right(A)
   A[0],A[1]...A[i-1]   | A[i],A[i+2]...A[m-1]
其中len(left(A)) + len(right(A)) = m

把B分成两部分:
      left(B)            |        right(B)
   B[0],B[1]...B[j-1]   | B[j],B[j+2]...B[n-1]
其中len(left(B)) + len(right(B)) = n

2. 所以我们把两个数组结合到一起
      left_part  |  right_part
   left(A)+left(B) | right(A)+right(B)
其中:len(left_part) = i + j; len(right_part) = m-i+n-j
要找到AB两个数组的中位数,则需满足:
len(left_part) == len(right_part);如果m+n总长度为奇数则这里把多出的哪一个元素放在左边,所以len(left_part) == len(right_part)+1
max(left_part) <= min(right_part);由于数组A和数组B都是有序数组,则需满足A[i-1] <= B[j], B[j-1] <= A[i]
转换成公式如下:
i+j = m-i+n-j+1
i+j = (m+n+1)/2
j = (m+n+1)/2 - i; 此时当i=m临界值时,j = (n-m+1)/2, 所以n >= m

3. 先通过二分法方式定位数组A中的一个元素,再通过上面的公式也就能确定数组B中的元素了
设imin := 0; imax := m,在[imin,imax]中进行搜索
i = imin+imax / 2; 则 j = m+n+1+imin+imax / 2才能满足len(left_part) == len(right_part)
此时我们已经满足了len(left_part) == len(right_part)
下面需要满足A[i-1] <= B[j]&& B[j-1] <= A[i]
但是我们会遇到不满足的情况
比如:
如果A[i-1] > B[j],则i需要减少,i和j是成反相关的,所以j就会增加,通过二分法,则i会向左边分,i= i+imin / 2
如果B[j-1] > A[i],则j需要减少,i则需要增加,i会向右边分, i = i + imax / 2

代码实现:

// findMedianSortedArrays查找两个数组的中位数
func findMedianSortedArrays(A []float64, B []float64) float64 {
	// 通过上面的分析得,len(B) >= len(A)
	if len(A) > len(B) {
		temp := A
		A = B
		B = temp
	}

	m := len(A)
	n := len(B)
	// 通过上面的分析,通过二分法先锚定A中的i
	imin := 0
	imax := m

	for imin <= imax {
		i := (imin + imax) / 2
		j := (m+n+1)/2 - i
		//
		if A[i-1] > B[j] && i > imin{
			// i需要减少,则下一个i在二分法的左边
			imax = i
		} else if B[j-1] > A[i] && i < imax{
			// i需要增加,则下一个i在二分法中的右边
			imin = i
		} else {
			// 表示满足条件A[i-1] <= B[j]&& B[j-1] <= A[i]

			// 计算中位数,找到左边的最大值和右边的最小值
			leftMax := 0.0
			if i == 0  {
				// 表示左边只有B
				leftMax = B[j-1]
			} else if j == 0 {
				// 表示左边只有A
				leftMax = A[i-1]
			} else {
				leftMax = math.Max(A[i-1],B[j-1])
			}
			if (m+n)%2 == 1 {
				return leftMax
			}

			// 找到右边的最小值
			rightMin := 0.0
			if i == m {
				// 表示右边只有B
				rightMin = B[j]
			} else if j == n {
				// 表示右边只有A
				rightMin = A[i]
			} else {
				rightMin = math.Min(A[i],B[j])
			}
			return (leftMax + rightMin) / 2
		}

	}
	// 表示没有找到
	return 0.0
}

求两个有序数组的中位数_第1张图片

 

你可能感兴趣的:(算法,go,算法,中位数)