数据结构【排序】| 归并排序

https://www.cnblogs.com/xiangers/archive/2021/10/25/15458333.html
https://blog.csdn.net/qq_33487573/article/details/106649345

归并排序法:是采用分治法的一个非常典型的应用。

分治法:

  • 分割:递归地把当前序列平均分割成两半。
  • 集成:在保持元素顺序的同时将上一步得到的子序列集成到一起(归并)。

偶数:
在这里插入图片描述
奇数:
在这里插入图片描述
合并两个有序数组:

方法一:单独创建了一个数组

def _merge(a: list, b: list) -> list:
    """Merge two sorted list"""
    c = []
    while len(a) > 0 and len(b) > 0:
        if a[0] < b[0]:
            c.append(a[0])
            a.remove(a[0])
        else:
            c.append(b[0])
            b.remove(b[0])
 
    if len(a) == 0:
        c += b
    else:
        c += a
    return c

方法二:原地变换,见文末最后一节

数据结构【双指针、排序】| leetcode 88. 合并两个有序数组(简单)

def _merge_sorted(nums: list) -> list:
    # Won't sort in place  进行分割,分成两对单个元素数组
    if len(nums) <= 1:
        return nums
    m = len(nums) // 2
    a = _merge_sorted(nums[:m])
    b = _merge_sorted(nums[m:])
    return _merge(a, b) 
 
# Wrapper
def merge_sorted(nums: list, reverse=False) -> list:
    import time
    start = time.time()
    """Merge Sort"""
    nums = _merge_sorted(nums)
    if reverse:
        nums = nums[::-1]
    t = time.time() - start
    return nums, len(nums), t
 
lst = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
lst = merge_sorted(lst, reverse=True)[0]
print(lst)

数据结构【双指针、排序】| leetcode 88. 合并两个有序数组(简单)

代码链接:https://leetcode.cn/problems/merge-sorted-array/solution/fu-xue-ming-zhu-dong-hua-ti-jie-cong-hou-teq6/

本题重点:

  • 两个数组有序;
  • 合并结果放到 nums1 中

注意题目给出的数组的方式:nums1 中本身有效的数字是前 m 位,nums1 的长度是 m+n,因此正好可以放下 nums1[0:m] + nums2[0:n]。

合并两个有序数组,我们第一反应肯定是想到了归并排序。归并排序是把两个有序的数组合并、放到另外一个数组中。所以空间复杂度是 O(M+N) 的。

由于本题给出的 nums1 是能够保证其长度能够放得下 m+n 个元素的,所以可以直接把合并的结果放到 nums1 中。

  • 思路一:如果两个数组从开头向结尾(数字从小到大)进行比较,那么每次把比较之后的数字放置到 nums1 中的前面,则需要把 nums1 中第 k 个位置后面的元素向后移动。移动次数比较多。
  • 思路二:如果两个数组从结尾向开头(数字从大到小)进行比较,那么每次把比较之后的数字放置到 nums1 中的后面,由于后面的数字本身就是提供出来的多余的位置,都是 0,因此不需要对 nums1 进行移动。

显然思路二更好。

从后向前进行比较

确定了主要的思路之后,实现起来其实很简单。

1、当 m > 0 m > 0 m>0 并且 n > 0 n > 0 n>0 时,从后向前比较 n u m 1 [ m − 1 ] num1[m−1] num1[m1] n u m s 2 [ n − 1 ] nums2[n−1] nums2[n1]

  • 如果是 n u m s 1 [ m − 1 ] nums1[m−1] nums1[m1] 大,则把 n u m s 1 [ m − 1 ] nums1[m−1] nums1[m1] 放到 n u m 1 num1 num1 的第 m + n − 1 m+n−1 m+n1 位置,并让 m − = 1 m−=1 m=1
  • 如果是 n u m s 1 [ n − 1 ] nums1[n−1] nums1[n1] 大,则把 n u m s 2 [ n − 1 ] nums2[n−1] nums2[n1] 放到 n u m 1 num1 num1 的第 m + n − 1 m+n−1 m+n1 位置,并让 n − = 1 n−=1 n=1

2、当上面的遍历条件结束的时候,此时 m 和 n 至少有一个为 0。

  • m = = 0 m== 0 m==0 时,说明 n u m 1 num1 num1 的数字恰好用完了,此时 n u m s 2 nums2 nums2 可能还剩元素,需要复制到 n u m s 1 nums1 nums1 的头部;
  • n = = 0 n== 0 n==0 时,说明 n u m 2 num2 num2 的数字恰好用完了,此时 n u m s 1 nums1 nums1 可能还剩元素,由于剩余的这些元素一定是 n u m s 1 nums1 nums1 n u m s 2 nums2 nums2 中最小的元素,所以不用动,直接留在原地就行。
class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        k = m+n-1
        while m > 0 and n > 0:
            if nums1[m-1] <= nums2[n-1]:
                nums1[k] = nums2[n-1]
                n -= 1
            else:
                nums1[k] = nums1[m-1]
                m -= 1
            k -= 1
        if m == 0:
            nums1[:n] = nums2[:n]

复杂度分析:

  • 时间复杂度: O ( m + n ) O(m+n) O(m+n)。指针移动单调递减,最多移动 m + n m+n m+n 次,因此时间复杂度为 O ( m + n ) O(m+n) O(m+n)
  • 空间复杂度: O ( 1 ) O(1) O(1)。直接对数组 n u m s 1 {nums}_1 nums1原地修改,不需要额外空间。

标题

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值