Leetcode算法题:两个有序数组求中位数

Leetcode算法题:两个有序数组求中位数

  • 要求时间复杂度为O(log(m+n))

思路:

  1. 暴力解决:合并数组并排序,简单且一定能实现,时间复杂度O(m+n)
  2. 由于两个数组已经排好序,可一边排序一边合并,用时为第一种的一半,时间复杂度依然为O(m+n)
  3. 由题目,只需要找中位数,即中位数两侧的元素并不需要完全排序,且两数组长度已知且有序,合并后中位数的索引是固定的,所以只要找到这一个或计算中位数的两个数的索引即可,既然为查找,可用二分查找,即可实现时间复杂度为O(log(m+n))

解决方案:

  • 将两数组分别分为左右两部分,左侧的两部分即为包含中位数的左侧,右侧即为可能包含中位数的右侧。由于一个数组的划分位置确定,另一个数组划分位置即可确定,所以只需使用二分查找在一个数组中查找到正确的划分位置就行了。

  • 为了防止一个数组中确定界限后另一个数组的分界线数组越界,直接在取小数组作为查找目标。

  • 递归查找很简单,但是索引在边界时情况非常复杂,且python中-1索引也可以取到,花了很久才找到问题所在,因此在此记录下来。


def findMedianSortedArrays(nums1, nums2):

    def mid_index(left, right):
        return (left + right) // 2
    
    # 以下称第一个数组为A,第二个数组为B
    def findMedianSortedArraysSorted(short_nums, m, long_nums, n):
        is_odd = (m + n) % 2 
        mid = (m + n - 1) // 2
        if m == 0:
            if is_odd:
                return long_nums[mid]
            else:
                return float(long_nums[mid] + long_nums[mid + 1]) / 2
        left = 0
        right = m - 1
        i = mid_index(left, right)
        while left <= i <= right:
            j = mid - (i + 1)
            # 若A数组左侧最大元素大于B数组右侧最小元素,中位索引左移
            if i > 0 and short_nums[i] > long_nums[j + 1]:
                right = i
                i = mid_index(left, right)
            # 若B数组左侧最大元素大于B数组右侧最小元素,中位索引右移
            elif i < m - 1 and long_nums[j] > short_nums[i + 1]:
                left = i + 1
                i = mid_index(left, right)
            # 从0索引开始查找,查找并未结束
            # 其实只有数组长度为2时才会出现这种情况
            elif i == 0 and m > 1 and long_nums[j] > short_nums[i + 1]:
                left = i + 1
                i = mid_index(left, right)
            # 达成结束条件
            else:
                if i == 0:
                	# 该数组最小值比在另一个数组中可能的中位值大
                	# 即整个数组所有元素都应该放在右侧
                    if short_nums[i] > long_nums[j + 1]:
                        max_left = long_nums[j + 1]
                        if j + 3 <= n:
                            min_right = min(short_nums[i], long_nums[j + 2])
                        else:
                            min_right = short_nums[i]
                    else:
                    	# 只出现在两个只有一个元素,且A数组元素小于B数组元素的情况下
                    	# 应该可以合并到其他情况中,但是脑子不够用了,希望有人可以提意见
                        if j == -1:
                            max_left = short_nums[i]
                            min_right = long_nums[j + 1]
                        else:
                            max_left = max(short_nums[i], long_nums[j])
                            if m > 1:
                                min_right = min(short_nums[i + 1], long_nums[j + 1])
                            else:
                                min_right = long_nums[j + 1]
                # 检索到了最后一个元素
                elif i == m - 1:
                	# 大坑,两数组等长时,检索到末端时j的值为-1
                	# python列表的-1索引可以取到最后一个元素,debug才找到问题
                    if j == -1:
                        max_left = short_nums[i]
                        min_right = long_nums[j + 1]
                    # A数组最大值小于B数组中位数
                    elif short_nums[i] < long_nums[j]:
                        max_left = long_nums[j]
                        min_right = long_nums[j + 1]
                    else:
                        max_left = max(short_nums[i], long_nums[j])
                        min_right = long_nums[j + 1]
                # 在中间位置找到正确的中位数
                else:
                    max_left = max(short_nums[i], long_nums[j])
                    min_right = min(short_nums[i + 1], long_nums[j + 1])
                if is_odd:
                    return max_left
                else:
                    return float(max_left + min_right) / 2
    a = len(nums1)
    b = len(nums2)
    if a > b:
        return findMedianSortedArraysSorted(nums2, b, nums1, a)
    else:
        return findMedianSortedArraysSorted(nums1, a, nums2, b)

你可能感兴趣的:(算法)