LeetCode 88. 合并两个有序数组

本文介绍了LeetCode第88题——合并两个有序数组的解题思路,包括直接排序、插入排序、希尔排序、冒泡排序、快速排序、选择排序和堆排序等经典算法。此外,还探讨了双指针方法,特别是逆向双指针的优化技巧,分析了算法的时间复杂度和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

88. 合并两个有序数组

题目:
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n,分别表示 nums1 和 nums2 中的元素数目。

请你合并 nums2 到 nums1 中,使合并后的数组同样按非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/merge-sorted-array

个人思路
  1. 最直观的思路就是把nums2的数字加入到num1的列表中,然后排序,这里我采用sort()排序。好了,你可以回去等通知了(不是
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.
        """
        for i in range(n):
            nums1[m+i] = nums2[i]
        nums1.sort()
  1. 这里顺便复习一下排序算法
    插入排序:
    (1)插入排序:
    将一条待排序的记录,将其关键字值的大小插入到前面已经排好序的记录序列中的适当位置,直到全部记录插入完成为止
def insertSort(cur):
    for i in range(1,len(cur)):
        temp = cur[i]   # 待插入的第i条记录
        for j in range(i-1,-2,-1):
            if cur[j] > temp:
                cur[j+1] = cur[j]   # 将前面比cur[i]大的记录往后移动
            else:
                break
        cur[j+1] = temp   # 将r[i]插入到第j+1个位置
def insertSort2(cur):
    for i in range(1,len(cur)):
        temp = cur[i]
        j = i - 1
        while j >= 0:
            if cur[j] > temp:
                cur[j+1] = cur[j]
                j = j -1
            else:
                break
        cur[j+1] = temp       

时间复杂度:O(n*n)
空间复杂度:O(1)

(2)希尔排序:插入排序的改进,但希尔排序是非稳定排序算法
主要思想:将列表分为n个小列表,分别进行插入排序,逐渐减少子列表长度直至为1
主要步骤:
① 选择一个增量序列d0,d1,d2…d(k-1)
② 根据当前增量di将n条记录分成di个子表,每个子表中记录的下标间隔为di
③ 对每个子表中的记录进行直接插入排序
④ 令i = 0, 1, … , k-1, 重复②~④

def shellSort(arr): 
    n = len(arr)
    gap = int(n/2)
    while gap > 0: 
        for i in range(gap,n): 
            temp = arr[i] 
            j = i 
            while  j >= gap and arr[j-gap] >temp: 
                arr[j] = arr[j-gap] 
                j -= gap 
            arr[j] = temp 
        gap = int(gap/2)

交换排序:
(1)冒泡排序:
重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。

def bubbleSort(arr):
    n = len(arr)
    # 遍历所有数组元素
    for i in range(n):
        # Last i elements are already in place
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1] :
                arr[j], arr[j+1] = arr[j+1], arr[j]

(2)快速排序
快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。
步骤为:
① 挑选基准值:从数列中挑出一个元素,称为"基准"(pivot);
② 分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成;
③ 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。
递归到最底部的判断条件是数列的大小是零或一,此时该数列显然已经有序。

def partition(arr,low,high): 
    i = ( low-1 )         # 最小元素索引
    pivot = arr[high]     
    for j in range(low , high): 
        # 当前元素小于或等于 pivot 
        if   arr[j] <= pivot: 
            i = i+1 
            arr[i],arr[j] = arr[j],arr[i] 
    arr[i+1],arr[high] = arr[high],arr[i+1] 
    return ( i+1 ) 
# arr[] --> 排序数组
# low  --> 起始索引
# high  --> 结束索引
  
# 快速排序函数
def quickSort(arr,low,high): 
    if low < high: 
  
        pi = partition(arr,low,high) 
  
        quickSort(arr, low, pi-1) 
        quickSort(arr, pi+1, high) 

选择排序:
(1)直接选择排序:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

import sys 
A = [64, 25, 12, 22, 11] 
  
for i in range(len(A)): 
      
   
    min_idx = i 
    for j in range(i+1, len(A)): 
        if A[min_idx] > A[j]: 
            min_idx = j 
                
    A[i], A[min_idx] = A[min_idx], A[i] 
  
print ("排序后的数组:") 
for i in range(len(A)): 
    print("%d" %A[i]),

(2)堆排序:
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。

def heapify(arr, n, i): 
    largest = i  
    l = 2 * i + 1     # left = 2*i + 1 
    r = 2 * i + 2     # right = 2*i + 2 
  
    if l < n and arr[i] < arr[l]: 
        largest = l 
  
    if r < n and arr[largest] < arr[r]: 
        largest = r 
  
    if largest != i: 
        arr[i],arr[largest] = arr[largest],arr[i]  # 交换
  
        heapify(arr, n, largest) 
  
def heapSort(arr): 
    n = len(arr) 
  
    # Build a maxheap. 
    for i in range(n, -1, -1): 
        heapify(arr, n, i) 
  
    # 一个个交换元素
    for i in range(n-1, 0, -1): 
        arr[i], arr[0] = arr[0], arr[i]   # 交换
        heapify(arr, i, 0) 
  
arr = [ 12, 11, 13, 5, 6, 7] 
heapSort(arr) 
n = len(arr) 
print ("排序后") 
for i in range(n): 
    print ("%d" %arr[i]),

注:参考菜鸟教程

  1. 双指针
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.
        """
        temp = []
        x = 0
        y = 0
        while x < m or y < n:
            if x == m:
                temp.append(nums2[y])
                y += 1
            elif(y == n):
                temp.append(nums1[x])
                x += 1
            elif(nums1[x]>nums2[y]):
                temp.append(nums2[y])
                y += 1
            else:
                temp.append(nums1[x])
                x += 1
        # 因为题目说的是原地修改nums1,所以使用nums1=temp是不能通过的
        nums1[:] = temp

复杂度分析

时间复杂度:O(m+n)。指针移动单调递增,最多移动 m+n次,因此时间复杂度为 O(m+n)。
空间复杂度:O(m+n)。需要建立长度为 m+n的中间数组 temp。

其他思路
  1. 逆向双指针
    双指针中,之所以要使用临时变量,是因为如果直接合并到数组nums 1 中,nums 1中的元素可能会在取出之前被覆盖。那么如何直接避免覆盖 nums1中的元素呢?观察可知,nums1的后半部分是空的,可以直接覆盖而不会影响结果。因此可以指针设置为从后向前遍历,每次取两者之中的较大者放进nums1
    的最后面。
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.
        """
        p1, p2 = m - 1, n - 1
        tail = m + n - 1
        while p1 >= 0 or p2 >= 0:
            if p1 == -1:
                nums1[tail] = nums2[p2]
                p2 -= 1
            elif p2 == -1:
                nums1[tail] = nums1[p1]
                p1 -= 1
            elif nums1[p1] > nums2[p2]:
                nums1[tail] = nums1[p1]
                p1 -= 1
            else:
                nums1[tail] = nums2[p2]
                p2 -= 1
            tail -= 1

复杂度分析

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

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/merge-sorted-array/solution/he-bing-liang-ge-you-xu-shu-zu-by-leetco-rrb0/
来源:力扣(LeetCode)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值