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. 合并两个有序数组(简单)
本题重点:
- 两个数组有序;
- 合并结果放到 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[m−1] 和 n u m s 2 [ n − 1 ] nums2[n−1] nums2[n−1]:
- 如果是 n u m s 1 [ m − 1 ] nums1[m−1] nums1[m−1] 大,则把 n u m s 1 [ m − 1 ] nums1[m−1] nums1[m−1] 放到 n u m 1 num1 num1 的第 m + n − 1 m+n−1 m+n−1 位置,并让 m − = 1 m−=1 m−=1。
- 如果是 n u m s 1 [ n − 1 ] nums1[n−1] nums1[n−1] 大,则把 n u m s 2 [ n − 1 ] nums2[n−1] nums2[n−1] 放到 n u m 1 num1 num1 的第 m + n − 1 m+n−1 m+n−1 位置,并让 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原地修改,不需要额外空间。