leetcode题解记录 [4, 88]

  1. 合并两个有序数组
class No_0088_Merge:
    """
    题干:
        https://leetcode.cn/problems/merge-sorted-array/
        两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目
        合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列
        题目中要求合并后数组不应由函数返回,而是存储在数组nums1中, nums1预留了足够的0

    解析:
        两个数组分别用一个指针记录当前位置,比较指针位置的值,输出后指针向后移动。
        例如:A=[*1,3,5], B=[*2,6,8], *号代表指针
        1<2,输出:1, 状态:[1,*3,5], [*2,6,8]
        2<3,输出:2, 状态:[1,*3,5], [2,*6,8]
        3<6,输出:3, 状态:[1,3,*5], [2,*6,8]
        5<6,输出:5, 状态:[1,3,5],  [2,*6,8]
        A已经完成,顺序输出B的剩余元素

    """

    @staticmethod
    def merge(nums1: list, m: int, nums2: list, n: int):
        p0, p1, p2 = 0, 0, 0

        # 复制num1, 原num1作为结果空间
        _nums1 = nums1.copy()
        while p1 < m and p2 < n:
            # 利用有序放入新数组中
            if _nums1[p1] <= nums2[p2]:
                nums1[p0] = _nums1[p1]
                p1 += 1
            else:
                nums1[p0] = nums2[p2]
                p2 += 1
            p0 += 1
        if p1 < m:
            nums1[p0:] = _nums1[p1:m]
        elif p2 < n:
            nums1[p0:] = nums2[p2:n]
        return nums1

    @staticmethod
    def merge_2(nums1: list, m: int, nums2: list, n: int):
        # 按题目给出的条件,可以逆向操作,向nums1的末尾填充,提高空间效率
        p0, p1, p2 = m+n-1, m-1, n-1

        while p1 >= 0 and p2 >= 0:
            if nums1[p1] >= nums2[p2]:
                nums1[p0] = nums1[p1]
                p1 -= 1
            else:
                nums1[p0] = nums2[p2]
                p2 -= 1
            p0 -= 1
        if p1 < 0:
            nums1[:p0+1] = nums2[:p2+1]
        elif p2 < 0:
            nums1[:p0+1] = nums1[:p1+1]
        return nums1
  1. 寻找两个正序数组的中位数
class No_0004_FindMedianSortedArrays:
    """
    题干:
        https://leetcode.cn/problems/median-of-two-sorted-arrays/
        寻找两个正序数组的中位数
    """
    @staticmethod
    def findMedianSortedArrays(nums1: List[int], nums2: List[int]):
		# 暴力解,合并数据
        all_nums = nums1 + nums2
        # 排序
        all_nums.sort()
		# 返回中位数
        size = len(all_nums)
        if size < 2:
            return all_nums[0]
        elif size % 2 == 0:
            return (all_nums[int(size/2)-1] + all_nums[int(size/2)]) / 2
        else:
            return all_nums[int(size/2)]

    @staticmethod
    def findMedianSortedArrays_2(nums1: List[int], nums2: List[int]):
        """
        解析:
        	暴力解完全没有使用到数组有序的条件,可以参考88题合并有序数组, 时间复杂O(m+n)
        	实际上并不需需要完成数据的合并,当数组长度到达中位时就可以返回
        """
        m, n = len(nums1), len(nums2)
        mid = (m+n) / 2
        p0, p1, p2 = -1, 0, 0

        all_nums = [0] * (m+n)
        while (p1 < m and p2 < n) and p1+p2 <= mid+1:  # 只需要到一半就可以退出循环了
            if nums1[p1] <= nums2[p2]:
                all_nums[p0] = nums1[p1]
                p1 += 1
            else:
                all_nums[p0] = nums2[p2]
                p2 += 1
            p0 += 1
            
        if p1+p2 <= mid+1:
            # 至少已经遍历完一个数组还没有达到一半总长度
            if p1 < m:
                all_nums[p0:] = nums1[p1:]
            elif p2 < n:
                all_nums[p0:] = nums2[p2:]

        if mid < 1:
            return all_nums[0]
        elif mid % 1 == 0:
            return (all_nums[int(mid-1)] + all_nums[int(mid)]) / 2
        else:
            return all_nums[int(mid)]

    @staticmethod
    def findMedianSortedArrays_3(nums1: List[int], nums2: List[int]):
        """
        划分数组:属于特定问题的特定解法,需要理解中位数的推导
        从位置i将A划分为数量近似相等的两部分 A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1],称为A左集和A右集
        从位置j将B划分为数量近似相等的两部分 B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1],称为B左集和B右集

        分别合并左集和右集, 两种情况:
        1、m+n为奇数:i+j = m-i + n-j + 1,∴ i+j = (m+n+1)/2,∴  j = (m+n+1)/2 - i,让
        2、m+n为偶数 i+j = m-i + n-j,∴ i+j = (m+n)/2 ,整数除法可以补上1,值不变,这样和上面的式子就统一表达,可以忽略奇偶性

        中位数的第二个性质,左集任意元素<=右集任意元素:
        1、判断:A左集的最大元素 <= B右集合的最小值
        2、判断:B左集的最大元素 <= A右集合的最小值
        如果不满足的话,也要调整分割线

        为减少处理分支,规定较短的数组为A,较长的数组为B,方面后续编码
        前一部分的最小值,和后一部分的最大值,就对应了合并后的中位数
        """
        m, n = len(nums1), len(nums2)
        if m > n:
            nums1, nums2 = nums2, nums1  # 交换数组,始终保持短数组在前,这样在[0, m]区间找分隔线
            m, n = n, m

        tol_left = int((m + (n-m+1)) / 2)  # 分隔线左侧要满足的元素个数, 这样写可以防止整形溢出

        # 定义num1的分隔线划在下标i元素的左侧, 所以下标i即分隔线左侧的元素个数,同理nums2的分割线为j
        # 结果要满足 nums1[i-1] <= nums2[j] and nums1[j-1] <= nums1[i]
        left, right = 0, m
        while left < right:     # 退出循环必然left == _r
            i = int(left + (right - left + 1)/2)     # 二分查找
            j = tol_left - i

            if nums1[i-1] > nums2[j]:
                # 左集最大大于右集最小, 分隔线左移
                right = i - 1
            else:
                left = i

        i = left    # 退出循环的时候找到满足nums1[i-1] <= nums2[j] and nums1[j-1] <= nums1[i]的位置
        j = tol_left-i

        # 分隔线左右的4个元素
        nums1_left_max = nums1[i-1] if i != 0 else float('-inf')  # 当i等于0时, 分隔线没有意义, 最小值保证比较结果
        nums1_right_min = nums1[i] if i != m else float('inf')    # 当i等于m时, 分隔线没有意义, 最大值保证比较结果
        nums2_left_max = nums2[j-1] if j != 0 else float('-inf')
        nums2_right_min = nums2[j] if j != m else float('inf')

        if (m+n) % 2 == 0:
            return (max(nums1_left_max, nums2_left_max) + min(nums1_right_min, nums2_right_min))/2
        else:
            return max(nums1_left_max, nums2_left_max)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值