LeetCode 88.合并两个有序数组

原题链接如下:
88. 合并两个有序数组
解法1(先合并后排序):

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        //先将nums2合并在nums1后面
        for(int i =m,j =0;i<m+n,j<n;i++,j++ ){
            nums1[i] = nums2[j];
        }
        sort(nums1.begin(),nums1.end());//切记c++中有自带sort函数,参数为(begin,end,cmp)其中cmp可有可无
    }
};

观察官方题解的细节之后,发现自己for循环里条件写得过于冗杂,不够简洁,于是代码修正如下:

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        //先将nums2合并在nums1后面
        for(int i=0i<n;i++){
            nums1[m+i] = nums2[i];
        }
        sort(nums1.begin(),nums1.end());//切记c++中有自带sort函数,参数为(begin,end,cmp)其中cmp可有可无
    }
};

解法2(双指针):

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        //采用双指针来进行解题
        int p1 = 0;
        int p2 = 0;
        int temp = 0;
        int result[m+n];
        while(p1<m||p2<n){
            //p1,p2中两个都结束了才可以退出while循环
            //接下来是先考虑单元素或者空元素的情况
            if(p1==m){
                //p1为单元素或者空元素
                temp = nums2[p2];
                p2++;
            }else if(p2 == n){
                //p2为单元素或者空元素
                temp = nums1[p1];
                p1++;
            }else if(nums1[p1]<nums2[p2]){
                temp = nums1[p1];
                p1++;
            }else {
                temp = nums2[p2];
                p2++;
            }
            result[p1+p2-1] = temp;
        }
        for(int i = 0;i<m+n;i++){
            //将最终结果拷贝到题目要求的nums1数组上
            nums1[i] = result[i];
        }
    }
};

解法3(后插):

观察讨论区小伙伴们的题解之后,发现由于nums1数组的特殊性,从后面开始确定结果数组会更加方便

因此题解2补充如下:

​ p1为nums1有数字部分的末尾,p2为nums2有数字部分的末尾,p为nums1的末尾

从p位置开始,每次将p1和p2位置的数字分别比较,大的就填入p位置当中。

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int len1= m-1;
        int len2= n-1;
        int len = m+n-1;
        while(len1>=0&&len2>=0){
            nums1[len--] = nums1[len1]>nums2[len2]?nums1[len1--]:nums2[len2--];
        }
        //如果没有执行while循环 则说明m和n当中有一个为0,此时无论是哪种情况,将nums2中数据拷贝到nums1中即可
        if(len1==-1){
            for(int i=0;i<=len2;i++){
                nums1[i] = nums2[i];
            }
        }
    }
};

本解法关键在于第11行到13行的代码,如果没有这三行,我们会发现,首先会出现

在这里插入图片描述

出现这个错误的原因是,如果一开始m就等于0,则len1赋初始值的时候,就为-1了,因此不会进入while循环,所以输出结果就是[0],而实际上并不是这样的,于是我立马抓住了m=0的情况,改进了一下代码:

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int len1= m-1;
        int len2= n-1;
        int len = m+n-1;
        while(len1>=0&&len2>=0){
            nums1[len--] = nums1[len1]>nums2[len2]?nums1[len1--]:nums2[len2--];
        }
		if(m==0){
            //既然nums1为空,则nums2就是所求题解,我们直接把nums2赋值到nums1上就可以了
			nums1 = nums2;
        }
    }
};

提交之后,又出现了新问题:

在这里插入图片描述

出现这种情况的原因是nums2中的数字太小了,从后面从大到小插,导致len1指针都到了nums1的头了,nums2中还有元素没能插进nums1中,到这里我们也就总结出了这个题会出现的两种情况:

​ 第一种是len2指针首先变为-1,这个时候说明nums2中的数字都比较大,直接比较早地就插入在了nums1的后端;不过还有可能是nums2一开始就没有元素,而nums1中有元素,此时m>0,而n=0,则len2=n-1,就先于len1一步变为-1了。对于这两种情况,无需处理,此时的nums1就是我们想要的答案,

​ 第二种是len1指针先变为-1,这个时候说明nums2中的数字比较小,没能插在nums1的后面,部分nums2中的元素需要从nums1的头部插进去,此时我们就需要再加上几行代码,题解评论中一个大佬用了java中的高级函数,本人由于弱鸡,不记得那些高级函数,就想着能不能朴素地用c++也完成这种情况的处理。通过仔细观察我们不难发现,len1先变为-1,所以我们可以采用判断语句,判断条件为len1是否等于-1,如果等于-1的话,我们需要把nums2中的元素从头到len2指针的位置搬到nums1中从头开始的位置,由于这个挪动复制动作,需要搬动的nums2中的元素有多少是与nums2密切相关的,所以我们就设计了如下代码:

if(len1==-1){
    for(int i=0;i<=len2;i++){
        nums1[i] = nums2[i];
    }
}

len1指针先变为-1还可能是因为在一开始,nums1就为空的,就是我们第一次提交出现的那种情况,此时m=0,所以len1也就是-1了,这种情况,同样也适用于上面的代码段,可以说上面的代码段就算是原博主用java写的

/ 表示将nums2数组从下标0位置开始,拷贝到nums1数组中,从下标0位置开始,长度为len2+1
        System.arraycopy(nums2, 0, nums1, 0, len2 + 1);

这一段的一个简单复现而已,只不过我没有用arraycopy这个高级函数而已。

综上算法改进,最后提交,得到AC结果如下:

执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户

内存消耗:8.9 MB, 在所有 C++ 提交中击败了33.62%的用户

通过测试用例:59 / 59

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值