Leetcode刷题:88. 合并两个有序数组(多种方法详细解读)


前言

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

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

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

提取主干
1.数组num1的初始长度为m+n,(其中m个元素为有效长度,剩下的n个元素全为0),数组nums2的长度为n.
2.数组nums1和nums2是按照递增顺序排列
3.合并nums1和nums2,同样按照递增顺序排列并存放在nums1中.

例子

输入: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 中的元素。

一、利用qsort快排

思路

先不按照顺序把nums2的数据全部塞到nums1中
目标:nums1={1,2,3,2,5,6};
然后运用qsort快排函数,使nums1变成应该递增的数组.
关于qsort的实现我在之前的博客讲过
qosrt快排函数的使用

代码实现

int Sort(const void* ptr1, const void* ptr2)
{
    return *(int*)ptr1 - *(int*)ptr2;
}

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
    for (int i = m; i < m + n; i++)
    {
        nums1[i] = nums2[i - m];//把nums2的数据全部塞到nums1中,i-m-->nums2[0]~nums2[n-1]
    }
    qsort(nums1, m + n, sizeof(int), Sort);
    //qsort(排序的地址,要排序的个数,每个元素的大小,排序的方法)
}

二、利用双指针

思路

这里需要运用辅助数组的方法:
也就是创建应该新数组,长度为m+n.
里面存放nums1和nums2合并且排序后的元素
最后将新数组的元素全部覆盖到nums1上

有的同学可能就要问了,为什么不在nums1上面直接排序呢?
原因

src指向的元素比dst指向的元素要大,数组要按顺序排列
所以dst指向的元素2会覆盖掉src的位置在这里插入图片描述
遇到这种情况也不是不能解决,我们将在第三种方法中进行讨论.

代码实现

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
    int tmp[m + n];//创建新数组
    int mid = 0;//要写进新数组的元素
    
    int src = 0;//nums1的下标
    int dst = 0;//nums2的下标
   
    while (src < m || dst < n)//只要src和dst没有走完就进入循环
    {
    	//可能会出现src已经走完nums1的有效长度m,而dst还没有走完.
        if (src == m)
        {
            mid = nums2[dst++];//将nums2的有效距离走完
        }
        else if (dst == n)
        {
            mid = nums1[src++];//将nums1的有效距离走完
        }


        else if (nums1[src] < nums2[dst])//找出两个元素的最小值
        {
            mid = nums1[src++];
        }
        else {
            mid = nums2[dst++];
        }
        tmp[src + dst - 1] = mid;//数组的元素下标从0开始
    }
    for (int i = 0; i < m + n; i++)
    {
        nums1[i] = tmp[i];//将新数组的元素全部覆盖到nums1
    }
}

三、利用逆序的双指针

思路

这里我们需要运用一点小技巧来解决上面nums2会覆盖掉nums1的有效元素
我们发现num1后n个元素全为0,从前到后排序不行,我们可以从后到前啊.
正好,将nums前面的一个有效元素移到后面去,就会产生一个空位,而空位的个数不变

代码实现

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
    int src = m - 1;//指向nums1最后一个有效元素
    int dst = n - 1;//指向nums2最后一个有效元素
    
    int tmp = 0;
    int cnt = m + n - 1;
    while (src >= 0 || dst >= 0)//src和dst从后到前全部遍历完
    {
        if (src == -1)//为什么会是-1呢?
        {
            tmp = nums2[dst--];
        }
        else if (dst == -1)
        {
            break;//第二个数组已经空了,而两个数组都是按照非递减排列的,说明剩下的第一个数组已经排列好了.
        }
        
        else if (nums2[dst] > nums1[src])//挑出大的元素放在nums1里面
        {
            tmp = nums2[dst--];
        }
        else
        {
            tmp = nums1[src--];
        }
        nums1[cnt--] = tmp;
    }

}

if (src == -1) 为什么会是-1呢?

在这里插入图片描述
当我们运行到这一步时,dst的元素>src的元素,所以我们要对cnt所指向的地方进行覆盖

在这里插入图片描述
覆盖完dst会从0减到-1,所以要进行if (src == -1)的判断,同理:else if (dst == -1)

总结

1.数据结构的精髓在于画图,大家在做题的时候要多画图.
2.解题思路最重要,好的解题思路能够极大地减低代码量
3.其次要搞清楚数组的下标.像方法三中的if (src == -1),我相信很多人会直接写成if (src == 0)

4.最后才是代码的实现,如果到这一步不行那就要多刷题.
在解决数组类的题目时,像方法二一样创建一个新的数组来充当中转站是很常见的,
这种做法能够大大减低代码的时间复杂度.提高代码运行效率.
关于双指针的命名重复度不要过高,不然容易在代码实现中搞混.

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海的宇宙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值