算法:寻找两个正序数组的中位数

思路:
利用中位数的特性,左边的数的个数=右边的数个数。
假设合并后的数组(实际上不会真的合并),中位数的位置是k,那么我们查找两个正序的数组,找到一个位置为i,另外一个位置为j,使得nums1[i] <= nums2[j+1] && nums1[i+1] >= nums2[j],由题设可知,nums1[:i] + nums2[:j] <= nums1[i+1:] + nums2[j+1:],
令i+j = k ,那么中位数就是nums1[:i]+nums2[:j]里面的最后一个元素,因为是有序数组,所以mid_num=max(nums1[i],nums2[j])
所以我们查找中位数的过程就变成了求解二元一次方程k=i+j,其中k已知
程序设计:
1、二分法移动查找nums1的i,则nums2的j=k-i
2、当nums1[i] > nums2[j+1] && nums1[i+1] >= nums2[j]时,说明i需要减小,j需要增大
3、当nums1[i] <= nums2[j+1] && nums1[i+1] < nums2[j]时,说明i需要增大,j需要减小
4、当nums1[i] > nums2[j+1] && nums1[i+1] < nums2[j]时,因为数组有序,nums1[i+1]>=nums1[i] && nums2[j+1]>= nums2[j],这种情况属于数据异常,即入参非题设要求的正序数组

func is_odd_number(num int) bool {
	b := num % 2
	if b == 0 {
		return false
	} else {
		return true
	}
}

func findMedianSortedArrays() (median float32) {
	nums1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	nums2 := []int{1,2,3,4,5,6}
	var max_nums []int
	var min_nums []int
	// 使用最小的数组进行查找效率更高
	if len(nums1) >= len(nums2) {
		max_nums, min_nums = nums1, nums2
	} else {
		max_nums, min_nums = nums2, nums1
	}
	max_nums_length := len(max_nums)
	min_nums_length := len(min_nums)
	total_length := max_nums_length + min_nums_length
	is_odd_event := is_odd_number(total_length)

	k := total_length / 2 // 无论奇偶数都取前面一个数进行处理,其偶数情况的中位数结果特殊处理即可
	if is_odd_event {
		k += 1
	}
	// 最小元素数组为0时,取最大的数组的k位置的为中位数
	if min_nums_length == 0 {
		if is_odd_event {
			median = float32(max_nums[k-1])
		} else {
			median = float32(max_nums[k-2] + max_nums[k-1])
		}
		return
	}

	var left, right, i int = 0, min_nums_length - 1, 0
	// 注意k=i+j 是一个数学上的数值关系表达式,下面的i、j均是转换成了数组的下标,所以i取不到min_nums_length,即right=min_nums_length-1
	for {
		i = (left + right) / 2 // 二分且是下标值
		j := k - i - 1 - 1     // i因为取下标本身就是再数值上面减1,而j=k-i表示的是数值,所以需-1得到j的数值,再减1则表示j下标

		var L1, R1, L2, R2 int
		L1 = min_nums[i]
		// 当没有i+1的元素时,取i表示
		if i+1 >= min_nums_length {
			R1 = min_nums[i]
		} else {
			R1 = min_nums[i+1]
		}
		L2 = max_nums[j]
		// 当没有j+1的元素时,取j表示
		if j+1 >= max_nums_length {
			R2 = max_nums[j]
		} else {
			R2 = max_nums[j+1]
		}

		if L1 <= R2 && R1 >= L2 {
			if L1 >= L2 {
				// 合并后的数组为奇数,取k元素即可
				if is_odd_event {
					L2 = L1
				} else {
					// 避免越界,取不到,L2自然就是排到k-1的位置
					if i-1 >= 0 {
						// 取第k-1元素
						if L2 < min_nums[i-1] {
							L2 = min_nums[i-1]
						}
					}
				}

			} else {
				if is_odd_event {
					L1 = L2
				} else {
					if j-1 >= 0 {
						if L1 < max_nums[j-1] {
							L1 = max_nums[j-1]
						}
					}
				}
			}
			return float32(L1+L2) / 2
		} else if L1 <= R2 && R1 < L2 {
			//i需要增大,j需要减小
			left = i + 1
			// 无法找到满足条件的i+j的关系,说明min_nums的全部小于中位数,中位数是max_nums数组的k-min_nums_length位
			if left >= right {
				if is_odd_event {
					// L1 = L2
					median = float32( max_nums[k-min_nums_length-1])
				} else {
					// L1 = R2
					median = float32(max_nums[k-min_nums_length-1] + max_nums[k-min_nums_length]) / 2
				}
				// mid = float32(L1+L2) / 2
				return
			}
		} else if L1 > R2 && R1 >= L2 {
			// i需要减小,j需要增大
			right = i - 1
			// 无法找到满足条件的i+j的关系,说明min_nums的全部大于中位数,中位数在max_nums的前k项
			if left > right {
				if is_odd_event {
					median = float32( max_nums[k-1])
				} else {
					median = float32( max_nums[k-1] + max_nums[k]) /2
				}
				return
			}
		} else {
			fmt.Println("数据不合法!")
			return
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值