给你两个按 非递减顺序 排列的整数数组 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]
- 示例 2:
- 输入:nums1 = [1], m = 1, nums2 = [], n = 0
- 输出:[1]
- 示例 3:
- 输入:nums1 = [0], m = 0, nums2 = [1], n = 1
- 输出:[1]
提示:
nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-109 <= nums1[i], nums2[j] <= 109
进阶:你可以设计实现一个时间复杂度为 O(m + n) 的算法解决此问题吗?
解法一:使用数组方法求解
var merge = function (nums1, m, nums2, n) {
nums1.splice(m, nums1.length - m, ...nums2);
nums1.sort((a, b) => a - b);
return nums1;
};
merge(nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3)
//输出值为(6) [1, 2, 2, 3, 5, 6]
该解法的核心思想通过数字的splice方法将nums1数组从第m位开始到末尾的元素全部移除,再通过扩展运算符(...)将nums2的数组元素填充至数组nums1上,再通过数字方法sort()对nums1进行排序则完成了两个有序数组的排序
知识点:
- 数组方法splice():实现对数组元素的删除、插入、替换功能
删除功能splice(a,b):a为删除元素的索引值,b为要删除的项数
插入功能splice(a,b,c):a为插入位置,b为0(意思为不删除元素),c为插入的元素
替换功能splice(a,b,c):a为起始位置,b为删除元素数量,c为插入元素
2.数组方法sort():实现对数组的排序功能
默认是从小到大排序
若要实现数值从大到小排序则需要在sort方法中传入一个函数为参数:
function f(a,b){
//return a-b;//默认返回值
return b-a;
}
var a=[3,5,1,5,2,6,8];
a.sort(f);//实现数组从大到小排列
3.扩展运算符的应用(...)
在上述解法中,需要将nums2数组元素依次插入到nums1中,这时不必将nums2元素全部写出来,只需要将在nums2前面加上扩展运算符(...)即将nums2中的元素展开依次添加至nums1中
该方法的时间复杂度为O((m+n)log(m+n));空间复杂度为O(log(m+n))。
解法二:双指针遍历
var merge1 = function (nums1, m, nums2, n) {
var a = 0, b = 0;
var sortArr = new Array(m + n).fill(0);//创建一个长度为m+n的,值全为0的数组
var cur;//定义一个当前值
while (a < m || b < n) {
if (a === m) {
cur = nums2[b++];
} else if (b === n) {
cur = nums1[a++];
} else if (nums1[a] < nums2[b]) {
cur = nums1[a++];
} else {
cur = nums1[b++];
}
sortArr[a + b - 1] = cur;
}
for (let i = 0; i < m + n; ++i) {
nums1[i] = sortArr[i];
}
return nums1;
};
merge1(nums1 = [1, 2, 3, 0, 0, 0], m = 3, nums2 = [2, 5, 6], n = 3)
//输出值为:(6) [1, 2, 2, 3, 5, 6]
该解法的思路是将nums1和nums2数组看成两个队列,使用两个指针依次指向两个队列的头部,通过比较两个指针指向的值,较小的值插入新的数组,同时指针指向队列的下一个元素,再进行比较,直到指针指向空值(即所有元素都已遍历完)。此时再通过遍历将nums1的值替换成新数组的值
知识点:
1.双指针算法的基本应用
2.for循环与while循环的区别
for循环知道循环次数;先判断再循环
while循环不知道循环次数,先判断再循环
do...while循环不知道循环次数,先循环再判断
该方法的时间复杂度:O(m+n);空间复杂度:O(m+n)。
解法三:逆向双指针遍历
var merge2 = function (nums1, m, nums2, n) {
var a = m - 1, b = n - 1;//定义两个指针
var tot = m + n - 1;//定义nums1数组下标索引
var cur;//定义一个当前值
while (a >= 0 || b >= 0) {
/*判断条件:
* 1.当a变为-1,则将b剩下的压入nums中
* 2.当b变为-1,则将a剩下的压入nums中
* 3.将a指向的数与b指向的数比较,小的则压入nums中
*/
if (a === -1) {
cur = nums2[b--];
} else if (b === -1) {
cur = nums1[a--];
} else if (nums1[a] > nums2[b]) {
cur = nums1[a--];
} else {
cur = nums2[b--]
}
nums1[tot--] = cur;
}
return nums1;
};
merge2(nums1 = [1, 2, 3, 0, 0, 0], m = 3, nums2 = [2, 5, 6], n = 3)
//输出值为:(6) [1, 2, 2, 3, 5, 6]
该解法的思路同样运用双指针的思想去对数组进行遍历,只不过是从两个数组的末尾开始进行比较,同时不需要创建一个新的数组去存储排序后的元素,直接从nums1的末尾开始排序。因为nums1始终是m+n的元素,其后n位元素全部为0,所以替换先从末尾开始替换不会对nums1数组的前m项产生影响。
该解法的时间复杂度:O(m+n);空间复杂度:O(1)。
拓展知识点:何为时间复杂度?何为空间复杂度?
- 时间复杂度可以说是一个模型,这个模型描述了算法的运行时间随问题规模 N 的变化情况。
- 空间复杂度是指一个算法在运算过程中临时占用存储空间大小的度量