Leetcode 88.合并两个有序数组-逆向双指针法(C语言) Merge Sorted Array

本篇文章为笔者的LeetCode刷题笔记。文章整体分为两部分:1.笔者自己思考的算法及代码。2.LeetCode官方给出的最优算法及代码。通过对比这两部分算法和代码的异同,笔者的算法思想和编程水平有了显著地提升。如果这篇文章能帮到你那真是再好不过了!

 一、笔者思考的解法及代码实现

A.算法:

1.双指针 ptr1, ptr2分别指向 nums1, nums2 最后一个数组元素(ptr1指向nums1第m-1个位置)

2.新malloc一个nums3数组,insert指针指向其下一个待插入位置(从最后一个位置开始插入)

3.分别将ptr1与ptr2所指的数组元素比较,大者插入insert指向的位置

4.当ptr1或ptr2有一个<0时(有一个数组已经遍历结束),将另一个数组中剩余元素依次放入nums3中

5.用nums3覆盖nums1

B.代码实现:

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
    int *nums3 = malloc(sizeof(int)*(m+n));
    int insert=m+n-1; //nums3数组待插入指针,初始化指向最后一个位置
    int ptr1=m-1, ptr2=n-1;
    for(int i=0;i<m+n;i++){        //遍历nums1数组
        if(ptr1>=0 && ptr2>=0){       //条件判断 nums1和nums2都未遍历完毕时进入(调整ptr1<0和ptr2<0顺序可将此步省略)
            if(nums1[ptr1]<=nums2[ptr2]){
                nums3[insert]=nums2[ptr2];
                ptr2--;
                insert--;
            }else if(nums1[ptr1]>nums2[ptr2]){
                nums3[insert]=nums1[ptr1];
                ptr1--;
                insert--;
            }
        }if(ptr1<0){        //当nums1遍历结束
            int leftnums = ptr2;
            for(int j=0;j<leftnums+1;j++){ //把nums2剩余元素依次加入nums3
                nums3[insert]=nums2[ptr2];
                ptr2--;
                insert--;
            }
            for(int k=0;k<m+n;k++){
                nums1[k]=nums3[k];
            }
        }else if(ptr2<0){ /当nums2遍历结束
            int leftnums = ptr1;
            for(int j=0;j<leftnums+1;j++){ //把nums2剩余元素依次加入nums3
            nums3[insert]=nums1[ptr1];
            ptr1--;
            insert--;
            }
            for(int k=0;k<m+n;k++){ //把nums3复制到nums1
                nums1[k]=nums3[k];
            }
        }    
    }
}

C.复杂度分析:

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

指针移动单调递减,最多移动m+n次

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

需要建立长度为m+n的中间数组nums3

D.算法缺点:1.没有利用nums1多余的n个空闲位,额外创建了nums3,增加了O(m+n)空间复杂度

二、官方最优解法及代码实现

A.算法:

在笔者算法的基础上,观察可知nums1的后半部分是空的,可以直接覆盖并且不会影响结果。因此可以指针设置为从后向前遍历,每次取两者之中的较大者放进nums1的最后面。

B.代码实现:

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
    int insert=m+n-1;
    int ptr1=m-1, ptr2=n-1;
    for(int i=0;i<m+n;i++){        //从后向前遍历nums1,也可换成while(ptr1>=0 || ptr2>=0) 条件循环)
        if(ptr1<0){        //将此句放在前面可以少些一个if判断,笔者自己的代码有提到
            nums1[insert--]=nums2[ptr2--];
        }else if(ptr2<0){        //将此句放在前面可以少些一个if判断,笔者自己的代码有提到
            nums1[insert--]=nums1[ptr1--];
        }else if(nums1[ptr1]<=nums2[ptr2]){    
            nums1[insert--]=nums2[ptr2--];        //注意理解insert--和ptr2--的作用
        }else if(nums1[ptr1]>nums2[ptr2]){
            nums1[insert--]=nums1[ptr1--];
        }
    }
}

C.复杂度分析:

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

指针移动单调递减,最多移动m+n次

空间复杂度:O(1)

直接对nums1修改,没有用到新数组,不需要额外空间

三、笔者小结,二者对比

A.算法对比:

1.想到了双指针尾插法,但忽略了nums1有多余的n个位置未利用

B.代码对比:

1.用for(int i=0;i<m+n;i++)和while(ptr1>=0 || ptr2>=0)条件循环皆可

2.注意理解nums3[insert]=nums1[ptr1];
                ptr1--;
                insert--;

和nums1[insert--]=nums1[ptr1--];之间的差距,可以使代码更简洁高效

3.四种情况:ptr1<0,  ptr2<0,  nums1[ptr1]<=nums2[ptr2],  nums1[ptr1]>nums2[ptr2]

这四个条件判断的先后位置很重要,ptr1<0,  ptr2<0 应该放在前面。

Keep going and carry on! 

谢谢你看到这里!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值