LeetCode88. 合并两个有序数组
更新:2021年7月19日00:07:12
问题描述及示例
给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
提示:
nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-109 <= nums1[i], nums2[i] <= 109
我的题解
我的题解1(直接借助JavaScript数组的sort()
方法)
思路非常简单粗暴,就是把nums2
往nums1
身上拼接,然后再对nums1
调用sort()
函数进行排序。当然这应该不是最佳解法,但是感觉非常地易于理解。
/**
* @param {number[]} nums1
* @param {number} m
* @param {number[]} nums2
* @param {number} n
* @return {void} Do not return anything, modify nums1 in-place instead.
*/
var merge = function(nums1, m, nums2, n) {
// 把`nums2`往`nums1`身上拼接
for(let i = m; i < m+n; i++) {
nums1[i] = nums2[i-m];
}
// 我一开始没有设置sort函数中的比较函数,导致提交后显示用例没有全部通过
nums1.sort((a,b) => a - b);
};
提交记录
59 / 59 个通过测试用例
状态:通过
执行用时: 56 ms
内存消耗: 38.2 MB
时间:2021年7月19日00:07:12
更新:2021年8月12日12:27:00
既然是合并数组,那为什么不试试展开运算符呢?根据MDN文档的相关描述,展开运算符可以非常方便地实现两个数组的合并。
那么上面的for
循环那一部分就可以用下面这行代码代替:
nums1 = [...(nums1.slice(0,m)), ...nums2];
其作用也是把nums2
往nums1
身上拼接。
在控制台中进行测试也显示确实可以运用展开运算符。但是可惜的是,似乎在LeetCode似乎并不是很支持这个写法,我也搞不懂为什么,明明之前也在LeetCode中用过展开运算符来着。
以下是MDN文档相关说明:
/* 如果使用展开语法, 代码如下:
(请注意, 这里使用展开语法创建了一个新的 arr1 数组,
Array.unshift 方法则是修改了原本存在的 arr1 数组)*/
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1 = [...arr2, ...arr1]; // arr1 现在为 [3, 4, 5, 0, 1, 2]
【更新结束】
更新:2021年10月11日15:18:36
我的题解2(利用了数组“有序”这个条件)
更新:2021年8月16日19:37:52
我原先的解法属于是根本没有利用到数组“有序”这个条件。而下面的程序就充分地利用了这个已有的条件。
首先用变量length
存储nums
数组的长度,指针length-1
用于指向待填充的nums1
数组元素;指针m-1
用于指向nums1
数组中待比较的元素;指针n-1
用于指向nums2
数组中待比较的元素。指针length-1
由后往前一位位往左移动,并将nums1[m-1]
和nums2[n-1]
中的较大值填入nums1[length-1]
,因为nums1
和nums2
都是非严格递增的升序数组,所以它们末尾的元素一定是当前它们各自数组中的最大值,这样合并下来的数组就可以利用原有数组的有序的特性继续保持有序。
如果遇到m-1 < 0
的情况,说明当前nums2
中还有没被遍历过的元素,这时直接将这些剩余元素按顺序逐位并入nums1
数组空余出来的前部即可,合并完成后的nums1
就是满足要求的结果了,执行return
以终止程序;如果遇到n-1 < 0
的情况,说明当前nums1
中还有没被遍历过的元素,这时的nums1
就已经是满足要求的数组了,不用再做其他操作,执行return
以终止程序。
注意观察模板中的注释中说不需要返回什么东西,被修改后的
nums1
数组就是最后的结果。所以下面程序中的return
语句只是为了终止程序。
稍显冗余的解法
/**
* @param {number[]} nums1
* @param {number} m
* @param {number[]} nums2
* @param {number} n
* @return {void} Do not return anything, modify nums1 in-place instead.
*/
var merge = function(nums1, m, nums2, n) {
// 如果nums2为空数组,直接终止程序即可,nums1中元素即是满足要求的结果
if(nums2.length === 0) {
return;
}
let length = m + n;
while(length - 1 >= 0){
if(nums1[m-1] >= nums2[n-1]) {
nums1[length-1] = nums1[m-1];
m--;
} else {
nums1[length-1] = nums2[n-1];
n--;
}
// 注意length指针是每次比较之后都要往左移动一位,
// 但是m和n指针是根据比较结果来决定是否移动的
length--;
if(m-1 < 0 || n-1 < 0) {
if(m-1 < 0) {
// 下面这条语句和下面的for循环的功能其实是一样的,
// 但是当遇到[2, 0], 1, [1], 1这个测试用例时,LeetCode返回的结果是[2,2]
// 而在浏览器的开发者工具中调试却发现其返回的是预期的正常结果:[1,2]
// 改成下面的for循环后才提交通过……不知道是为什么,可能是展开运算符的问题吧
// nums1 = [...nums2.slice(0,n), ...nums1.slice(length)];
for(let i = 0; i < n; i++) {
nums1[i] = nums2[i];
}
// 注意这里要及时终止程序
return;
}
return;
}
}
};
提交记录
59 / 59 个通过测试用例
状态:通过
执行用时:80 ms, 在所有 JavaScript 提交中击败了54.07%的用户
内存消耗:37.5 MB, 在所有 JavaScript 提交中击败了97.40%的用户
时间:2021/08/16 19:37
更加精简的解法
思路和上面的一样,但是下面的解法大大美化了代码结构。是更好的写法。
const merge = function(nums1, m, nums2, n) {
let len1 = m - 1,
len2 = n - 1,
len = m + n - 1
while(len2 >= 0) {
if(len1 < 0) {
nums1[len--] = nums2[len2--]
continue
}
nums1[len--] = nums1[len1] >= nums2[len2] ? nums1[len1--]: nums2[len2--]
}
};
以上代码来自:
感谢博主分享!
【更新结束】
官方题解
更新:2021年7月29日18:43:21
因为我考虑到著作权归属问题,所以【官方题解】部分我不再粘贴具体的代码了,可到下方的链接中查看。
更新:2021年7月29日20:06:41
【更新结束】
有关参考
参考:箭头函数 - JavaScript | MDN
参考:JavaScript扩展运算符(…) - 一路向北√ - 博客园
参考:Javascript es6中扩展运算符(…)_冰河世纪-CSDN博客_javascript 扩展运算符
更新:2021年8月12日12:34:39
参考:展开语法 - JavaScript | MDN
更新:2021年8月16日19:44:25
参考:【微信公众号:高级前端进阶 2021-08-15】每日算法:合并两个有序数组