合并两个有序数组

题目描述

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

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

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

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。

示例 3:

输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

提示:

  • nums1.length == m + n
  • nums2.length == n
  • 0 <= m, n <= 200
  • 1 <= m + n <= 200
  • -109 <= nums1[i], nums2[j] <= 109

题目分析

 非递减顺序排列:关键字递增序排列,但是并非单调递增,就是从小到大且允许中间有重复的关键字。

这个题目的关键是不能新开辟一个数组,将两个数组中的关键字放进去。而是需要将重新排列好的

关键字顺序放回 nums1 数组。

题目解析

解法一:从头开始遍历        

        拿到这个题的最开始的思路就是,设两个指针 tmp1,tmp2,分别指向 nums1,nums2的第一个元素。

        如果 tmp2 指向的元素比 tmp1 小,就将 tmp2 指向的元素插入到 tmp1 指向元素的前面,然后将 nums1 数组中 tmp1 及以后的元素都往后移一步,然后将 tmp1 和 tmp2 加 1。加 1 的原因,tmp1 开始指向的数组索引是 0,现在索引为 0 的元素已经变成 nums2 数组中的第一个元素,所以要继续判断 tmp1 原本指向的元素和 nums2 数组中下一个元素的大小,所以需要将 tmp1 和 tmp2 均加 1。

        如果 tmp2 指向的元素大于等于 tmp1,就不做交换,然后将 tmp1 加 1,tmp2 不变,继续比较两个元素的大小。重复操作,直到遍历完 nums2 数组。     

        其实,通过上面的描述,很容易看出,这个算法是比较麻烦的,主要是在每次将 nums2 的元素插入 nums1 时,nums1 数组中的元素都需要往后挪,这会让时间复杂度大大提升。所以就有了下面的算法。

解法二:从尾开始遍历  

        这个算法的核心就是,从两个数组的最后面开始遍历。这样做相比从头开始遍历有什么好处呢?

        由于两个数组都是有序的,当比较两个数组的最后一个元素时,较大的那个元素是不是就是两个数组中最大的那个数,那这个数是不是应该放在 nums1 数组的最后一个位置,所以这里还需要设一个指针,指向 nums1 数组中未排好序的最后一个位置

        以下面这两个数组为例:

        通过第一次比较得出数组 2 中的 6 更大,将其放在 last 指向的位置;

        nums1[tmp1] = 3   <  nums2[tmp2] = 6,    nums1[last] = nums2[tmp2]

        放完过后,tmp2 指向的元素已经排好序了,那就要比较下一个数了,所以 tmp2--。同时,last 指向的位置也已经有序了,要把它重新指向最后一个未排好序的位置,所以 last--。

        而此时,tmp1 指向的元素由于还没有确定它应该待的位置,所以此时 tmp1 保持不变。

        当 tmp1 指向 3,tmp2 指向 2 时,last 指向 tmp1 + 1 的位置,此时,应该将 3 放到 last指向的位置,然后 tmp1--,last--,tmp2 不动。

        重复以上操作,直到将 nums2 数组中所有的元素全都放到 nums1 中。

         那了解了从尾遍历的大致思路过后,我们还需要考虑一些特殊的情况

        ① nums1 数组中没有元素

                直接将 nums2 数组中的全部元素挪到 nums1 即可,然后 return。

        ② nums2 数组为空

                那此时 nums1 数组就是最终结果,直接 return。

        ③ nums1 与nums2 数组比较时,nums1 数组已经遍历完,nums2 数组还未遍历完

                将 nums2 剩下的元素,直接挪到 nums1 的 last 指针及其前面的位置即可。

时间复杂度:O(m+n)

空间复杂度:O(1)

代码 

        在写代码的过程中还需要注意一个点:不能用 Arrays.copyOf 方法,直接将 nums2 数组拷贝给 nums1。

        因为 copyOf 这个方法是在堆中重新开辟了一个新数组 copy,然后将 nums2 的数据拷贝到这个新数组 copy 中,而 copyOf 方法返回的就是这个 copy 数组的引用(地址位置),那么当把这个方法的返回值重新赋值给 nums1 时,nums1 将不再存储原来的(题目所要求的) 数组的引用,而是这个新数组 copy 的引用。那么原数组中的元素还是没有改变。 

通过结果可以看到,nums1 数组并没有改变。

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        //nums1数组为空,直接将nums2元素挪到nums1即可
        if(m == 0){
            for(int i = 0; i < nums2.length; i++){
                nums1[i] = nums2[i];
            }
            return;
        }
        //nums2为空,直接返回
        if(n == 0){
            return;
        }
        //注意:last 指向的位置,即索引,应该是数组长度 -1;
        int last = m + n - 1;
        //同理,nums1 和 nums2 数组的最后一个元素的索引,也是长度 -1
        m--;
        n--;
        while(n >= 0 && m >= 0){
            int tmp1 = nums1[m];
            int tmp2 = nums2[n];
            if(tmp2 > tmp1){
                nums1[last] = tmp2;
                n--;
                last--;
            } else{
                nums1[last] = tmp1;
                m--;
                last--;
            }
        }
        //nums2数组还有剩余,全部放到nums1数组中
        while(n >= 0){
            nums1[last] = nums2[n];
            last--;
            n--;
        }
    }
}

      

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值