寻找两个正序数组的中位数——力扣算法系列4

2020.06.26—27 周五、周六
4寻找两个正序数组的中位数
这道题目在我点进去之前我看着“困难”两个大字,心生怯惧。点进去之后看了一遍题目,不过如此。脑子里瞬间想到归并找中位数。然鹅!!!困难是真的困难!时间复杂度O(log(m+n))千万不要忽略!!
网上有很多帖子时间复杂度O(m+n)就好意思贴上去。

首先介绍中位数的概念
一般我们理解的中位数是指一个有序数组里排在中间的数。
准确的定义是:选定一个数(不一定包含在数组里),让数组里大于和小于这个数的元素数量都不超过总元素数量的一半,这个数叫中位数。
比如:
1,2,3,4,5的中位数是3 (可以理解为3在中间;也可以说小于3的数有两个,大于3的数有两个,都不超过总数5的一半)
1,2,3,3,3的中位数是3 (可以理解为3在整个数组顺序上的中间)在这里会有疑问,小于3的数有两个可是大于3的数有零个。故实际操作定义如下:
总数为奇数,中位数就是有序序列下标在中间的那个数。
总数为偶数,中位数就是中间两个数的平均数。

——————————我是分割线——————————
题目:给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。

示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0

示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
——————————我是分割线——————————
解法:
思路:首先说一下错误的解法,也是大部分人第一时间想到的解法——归并,然后找到中位数(总数为奇数个则找最中间的那个数;总数是偶数个则找中间的两个求平均值)。当然这种解法的代码我也会放出来,但是声明!这种解法是不符合题意的!

# 不符合时间按复杂度O(log(m+n))
def findMedianSortedArrays(nums1, nums2):
	m = len(nums1)
	n = len(nums2)
	ls = []
	i=0
	j=0
	while i<m and j<n: #一旦有一个数组遍历完毕则完毕
		if nums1[i] < nums2[j]:
			ls.append(nums1[i])
			i = i + 1
		else:
			ls.append(nums2[j])
			j = j + 1
	if i==m: #如果nums1已经遍历完 则把nums2剩下的元素加进去
		ls = ls + nums2[j:n]
	if j==n:
		ls = ls + nums1[i:m]
	if len(ls)%2==1: #新数组奇数个元素
		return ls[len(ls)//2]
	else: #新数组偶数个元素 则中间两个元素取平均数
		return (ls[len(ls)//2] + ls[len(ls)//2-1]) / 2
nums1 = [1,2]
nums2 = [3,4]
print(findMedianSortedArrays(nums1,nums2))

——————我是可爱的分割线——————

接下来是二分法的解法:
思想:利用总体中位数的性质和左右中位数之间的关系来把所有的数先分成两堆,找出左堆最大和右堆最小,然后再在两堆的边界返回答案。
思想是这个思想没错的,但是我本地的编译器和系统的结果就是不一样,一直不让我过。在这里如果有看出问题的朋友们可以私信我!
放代码:

def findMedianSortedArrays(nums1, nums2):
	m = len(nums1)
	# print(m)
	n = len(nums2)
	# print(n)
	#如果nums1的长度m比nums2的长度n长,则交换位置,让nums2更长
	if m>n:
		nums1,nums2,m,n = nums2,nums1,n,m
	#如果长度更长的数组为空 则表示两个数组都为空 异常处理
	if n==0:
		raise ValueError

	i_min = 0
	i_max = m

	#二分法
	while (i_min <= i_max):
		i_mid = i_min + (i_max-i_min) // 2
		j_mid = (m + n - 2*i_mid) // 2
		# 左堆最大只可能是nums1[i_mid-1],nums2[j_mid-1]
        # 右堆最小只有可能是nums1[i_mid],nums2[j_mid]
        # 让左右堆相等需要满足的条件是i_mid + j_mid = (m-i_mid) + (n-j_mid) 即 j_mid = (m+n-2*i_mid)//2
        # 或者i_mid + j_mid = (m-i_mid) + (n-j_mid) + 1 因为总数为奇数,向下取整数操作,右堆会多1

        #数组一左堆最大大于数组二右堆最小,imid太大了,需小一点
		if(i_mid>0 and nums1[i_mid-1]>nums2[j_mid]):
			imax = imid - 1
		# 数组二左堆最大大于数组一右堆最小,imid太小了,需大一点
		elif(i_mid<m and nums1[i_mid]<nums2[j_mid-1]):
			i_min = i_mid + 1
		else:
			# 边界处理,依次得到左堆最大和右堆最小
			if(i_mid == m):
				minright = nums2[j_mid]
			elif(j_mid == n):
				minright = nums1[i_mid]
			else:
				minright = min(nums1[i_mid],nums2[j_mid])
			if(i_mid == 0):
				maxleft = nums2[j_mid-1]
			elif(j_mid == 0):
				maxleft = nums1[i_mid-1]
			else:
				maxleft = max(nums1[i_mid-1],nums2[j_mid-1])

            #如果总数是奇数,由于取中间数时向下取整,所以右堆多一,minright就是中位数
			if ((m+n)%2) == 1:
				return minright
            #如果总数是偶数,取左堆最大和右堆最小的平均值
			else:
				return (maxleft+minright)/2
nums1 = [1,2]
nums2 = [3,4]
print(findMedianSortedArrays(nums1,nums2))

放我本地编译器的运行结果:
在这里插入图片描述
这是系统编译的结果:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值