LeetCode-88-合并两个有序数组


题意描述:

给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。

说明:

  • 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
  • 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

输出: [1,2,2,3,5,6]

解题思路:
Alice: 这个简单啊,直接把nums2数组里面的元素复制到nums1里面,然后一起再排个序就好了。
Bob: 哦豁,可以这样写,但是这样时间复杂度就是O(n*log(n))了,应该还有更快的写法,两个数组都是有序的这个信息要用起来呀。
Alice: 两个数组都是有序的,排序的话应该会比“最坏情况”快不少吧。
Bob:我们可以遍历nums2中的元素,为每个元素找到一个插入的位置,然后右移nums1里面插入位置后面的元素,再插入。
Alice: 你这样每次都将后面的元素都右移一位,应该比排序更耗时吧。应该是O(n^2)了。
Bob: 是啊,要是能不右移一位,一次性找到所有元素的最终位置就好了。该怎么做呢?
Alice:双指针啊,就像那个什么合并两个有序链表一样。再开一块内存,放一个新数组,把两个数组合并,然后再把合并后的数组赋值到 nums1 的地址。
Bob: 这样跑起来是很快的O(m+n)的时间复杂度,也就遍历了两个数组,再加上最后赋值的一个遍历。
Alice: emmmm, 这样看来应该还有更好的解法,直接把nums2的数组元素插入到nums1的指定位置上去,这样就没有后面再复制数组的操作了。
Bob: 是啊,可是该怎么写呢?怎么找到准确的插入位置呢?可以直接计算出来吗?
Alice: 明儿再说吧,我妈喊我回家吃饭了。


代码:

Python 偷懒的方法:直接将nums2中的元素复制到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.
        """
        for x in range(m, len(nums1)):
            nums1[x] = nums2[x-m]
        nums1.sort()
        return

Python 解法二:遍历nums2中的每一个元素,为每一个元素在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.
        """
        endIndex = m-1
        for ele in nums2:
            insertPosition = self.findPosition(nums1, endIndex, ele)
            endIndex += 1
            # 每次插入一个元素后,nums1里面有序数组的长度就要增加一个
            nums1 = self.rightShiftOne(nums1, insertPosition)
            nums1[insertPosition] = ele
        return 
    

    def findPosition(self, nums: List[int], endIndex: int, ele: int) -> int:
        # 在有序数组nums[:endIndex+1] 中寻找一个 ele 插入的位置。注意 ele 很小或者 ele 很大的情况。
        if  ele <= nums[0]:
            return 0
        if ele >= nums[endIndex]:
            return endIndex + 1
        
        for x in range(1, endIndex+1):
            if nums[x-1] <= ele and nums[x] > ele:
                return x

    def rightShiftOne(self, nums: List[int], startIndex: int) -> List[int]:
        # 将nums数组中从startIndex下标开始的元素全部右移一位
        endIndex = len(nums) - 1
        for x in range(endIndex, startIndex, -1):
            nums[x] = nums[x-1]
        return nums

Python 解法三: 双指针法,不过我用了额外的 (m+n) 内存,时间复杂度当然很低了,只有O(m+n),是的,仅仅遍历了两个数组而已。但是,应该还有直接将nums2数组中的元素插入到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.
        """
        ret = []
        index1 = index2 = 0 
        while index1 < m or index2 < n:
            if index1 == m and index2 < n:
                ret.extend([nums2[x] for x in range(index2, n)])
                break
            if index1 < m and index2 == n:
                ret.extend([nums1[x] for x in range(index1, m)])
                break

            if nums1[index1] <= nums2[index2]:
                ret.append(nums1[index1])
                index1 += 1
            else:
                ret.append(nums2[index2])
                index2 += 1
            
        for x in range(m+n):
            nums1[x] = ret[x]
        return 

Python 解法三优化:还是双指针,使用了更少的内存。

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.
        """
        tmp = [nums1[x] for x in range(m)]
        index1 = index2 = index = 0

        while index1 < m and index2 < n:
            if tmp[index1] <= nums2[index2]:
                nums1[index] = tmp[index1]
                index1 += 1
            else:
                nums1[index] = nums2[index2]
                index2 += 1
            index += 1
        
        if index1 < m:
            nums1[index: index + m-index1] = tmp[index1:m]
        if index2 < n:
            nums1[index: index + n-index2] = nums2[index2:n]
        return

Python 方法四: 双指针,但是从数组末尾开始插入,不需要额外的空间,时间复杂度O(n)。

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.
        """
        index1 = m-1
        index2 = n-1
        index  = m + n - 1

        while index1 >= 0 and index2 >= 0:
            if nums1[index1] >= nums2[index2]:
                nums1[index] = nums1[index1]
                index1 -= 1
            else:
                nums1[index] = nums2[index2]
                index2 -= 1
            index -= 1

        if index2 >= 0:
            nums1[:index+1] = nums2[:index2+1]
        return

Java 方法一: 拼接 + 排序

class Solution {
    public void merge(int[] A, int m, int[] B, int n) {
        int aindex = m + n - 1;
        int bindex = n - 1;
        while(bindex >= 0){
            A[aindex--] = B[bindex--];
        }
        Arrays.sort(A);
    }
}

Java 方法二: 终极解法,O(n),反个方向就好了。有点像剑指 Offer 里面替换字符那道题的思路。

class Solution {
    public void merge(int[] A, int m, int[] B, int n) {
        int index = m + n - 1;
        n--;
        m--;
        while(n >= 0 && m >=0){
            if(A[m] > B[n]){
                A[index--] = A[m--];
            }else{
                A[index--] = B[n--];
            }
        }
        while(n >= 0){
            A[index--] = B[n--];
        }
    }
}

易错点:

  • 还是边界值和一些特殊情况:
[400,0, 0]
1
[1, 3]
2
[0, 0, 0]
0
[1, 3]
2
[1, 0, 0]
1
[2, 3]
2

总结:

  • 另一些一些测试用例。
[1,2,3,0,0,0]
3
[2,5,6]
3
[1,0]
1
[1]
1
[]
0
[]
0
[12]
1
[]
0
[0]
0
[1]
1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值