前端算法题----合并两个有序数组

算法题讲解

真题描述:

给你两个按 非递减顺序 排列的整数数组 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:

示例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 的末尾往前面填补。

这里有一点需要解释一下:

为什么是从后往前填补?

因为题目要求是把所有的值合并到 nums1 里,并不是返回一个新数组,所以说在这里 nums1 是一个长度可以存储nums2的容器。但是这个容器,它不是空的,而是前面个格子是有内容,但是后面是空的而且空的长度刚好可以放下nums2。如果我们从前往后填补,就会覆盖掉我们原来的值。而从后往前填补,我们填的都是没有内容的格子,这样会省掉很多麻烦。

由于 nums1 的数值位数和 nums2 并不一定是一样长的。所以我们还需要考虑其中一个数组提前到头的情况:

  1. 如果提前遍历完的数组是 nums1 ,剩下的是 nums2。那么这时意味着 nums1 的头部空出来了,直接把 nums2 整个补到 nums1 前面去即可。
  2. 如果提前遍历完的数组是 nums2,剩下的是 nums1。由于容器本身就是 nums1,此时不必做任何额外的操作。

方法一

代码

/**
 * @param {number[]} nums1
 * @param {number} m
 * @param {number[]} nums2
 * @param {number} n
 * @return {void} Do not return anything, modify nums1 in-place instead.
 */
const merge = function(nums1, m, nums2, n) {
    // 初始化两个指针的指向,初始化 nums1 尾部索引k
    let i = m - 1, j = n - 1, k = m + n - 1
    // 当两个数组都没遍历完时,指针同步移动
    while(i >= 0 && j >= 0) {
        // 取较大的值,从末尾往前填补
        if(nums1[i] >= nums2[j]) {
            nums1[k] = nums1[i] 
            i-- 
            k--
        } else {
            nums1[k] = nums2[j] 
            j-- 
            k--
        }
    }
    
    // nums2 剩下的情况,特殊处理一下 
    while(j>=0) {
        nums1[k] = nums2[j]  
        k-- 
        j--
    }
};

这个函数的时间复杂度为O(m+n),空间复杂度为O(1)。

代码讲解:
  • 定义一个merge函数。
  • 指针i、j、k分别初始化为nums1的末尾、nums2的末尾、nums1和nums2合并后的末尾
  • 然后定义一个while循环当i和j都大于0时进入循环,判断当nums1[i] >= nums2[j]时把nums1末尾的值填入 nums1的尾部
  • 否则把nums2末尾的值填入 nums1的尾部
  • 直到不满足i和j都大于0时退出循环
  • 判断一下nums2是否还有剩余值的情况,j是否大于0,如果nums2还有剩(j>0)把nums2的剩于的值按顺序赋值给num1剩余的空位

虽然双双指针可很好的帮解决问题,但是双指针法写起来实在笨重。那我们还有没有更加快捷、简单的解题方式呢?

当然有我们可以利用JavaScript的数组方法去解答

方法二:splice+sort

这种解法非常简单明了,直接使用了JavaScript内置的数组方法splicesort

方法二

代码

第二种解法,使用JavaScript内置的数组方法`splice``sort`var merge = function(nums1, m, nums2, n) { 
nums1.splice(m, nums1.length - m, ...nums2);
nums1.sort((a, b) => a - b); };

};
  • 首先,通过nums1.splice(m, nums1.length - m, ...nums2)nums2数组中的元素插入到nums1数组的后面,同时保证nums1数组的长度不变,这样就可以省去额外的空间开销。
  • 然后,调用nums1.sort((a, b) => a - b)将nums1数组按照从小到大的顺序排序,从而得到最终的结果。

总结

虽然这种解法非常简单,但是由于使用了sort方法,时间复杂度为 O ( ( m + n ) log ⁡ ( m + n ) ) O((m+n)\log(m+n)) O((m+n)log(m+n)),相对于双指针法的 O ( m + n ) O(m+n) O(m+n)来说效率会略低一些。此外,在实际使用中,由于sort方法是基于比较的排序算法,因此当输入规模较大时,可能会导致性能问题。

写在最后

伙伴们,如果你觉得我写的文章对你有帮助就给zayyo点一个赞👍或者关注➕都是对我最大的支持。当然你也可以加我微信:IsZhangjianhao,邀你进我的前端学习交流群,一起学习前端,成为更优秀的工程师~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zayyo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值