面试经典150题 001:合并两个有序数组
1.题目描述
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意: 最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
示例:
输入: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.思路展示
常规叠甲:这是本人在做这道题时候的思路,除非实在难以解决,否则不会轻易去借鉴他人的想法(毕竟是自己的博客,还是要以自己的想法为主),所以我的思路和方法有时候并不是最优解,也是希望通过我的思路能让大家发散性地思考,培养自己思考问题的能力,同时博采众长。
当我看到这道题的第一眼,我脑子里立马蹦出了双指针这个思路,因为这道题给出的两个vector是非递减的,所以我可以拿两个指针指向两个vector同时顺序遍历,互相比较,谁小谁放前面。显然,这样做,时间复杂度可以达到O(m + n)。
但是,题目明确指出,最后的返回结果是保存在nums1数组当中的,这就涉及到了另外一个方面,你是要选择原地操作还是另外开辟空间操作。
我一开始的思路还是比较直接的,另外开辟空间去做,最后将结果保存在nums1当中,以下是这种思路的展示图:
第二种思路就是原地在nums1数组上操作,这需要你对vector这个动态数组容器的操作非常熟悉。我们依然使用两个指针,同时遍历两个数组,当我们碰到nums2中的某个元素小于nums1当前元素时,我们就可以使用insert()函数进行插入了,同时为了保证nums1元素的个数为m + n,我们还需要使用pop_back()操作弹出末尾的0,思路展示图如下:
3.代码展示
Coding is a piece of art!
编程这条路上我们永远都要精益求精,结果并不是我们的最终目的。希望大家在参考我的代码后能够有更多启发,写出更加优美、效率更高的代码。
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
vector<int> copy(nums1.begin(), nums1.end());
int i = 0, j = 0;
nums1.clear();
while(i < m && j < n){
if(copy[i] <= nums2[j]){
nums1.push_back(copy[i]);
i++;
}
else{
nums1.push_back(nums2[j]);
j++;
}
}
if(i == m){
while(j < n){
nums1.push_back(nums2[j]);
j++;
}
}
else{
while(i < m){
nums1.push_back(copy[i]);
i++;
}
}
}
};
以上代码是另外开辟空间版,大家也可以尝试原地操作版,欢迎和我一起学习,一起交流!
4.关键点总结
- 双指针法是解决有序数组问题的经典方法。
- 原地合并通过vector动态数组进行操作,避免了额外空间的使用。
- 注意处理边界条件(如某一数组提前遍历完毕)。