原题链接如下:
88. 合并两个有序数组
解法1(先合并后排序):
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
//先将nums2合并在nums1后面
for(int i =m,j =0;i<m+n,j<n;i++,j++ ){
nums1[i] = nums2[j];
}
sort(nums1.begin(),nums1.end());//切记c++中有自带sort函数,参数为(begin,end,cmp)其中cmp可有可无
}
};
观察官方题解的细节之后,发现自己for循环里条件写得过于冗杂,不够简洁,于是代码修正如下:
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
//先将nums2合并在nums1后面
for(int i=0i<n;i++){
nums1[m+i] = nums2[i];
}
sort(nums1.begin(),nums1.end());//切记c++中有自带sort函数,参数为(begin,end,cmp)其中cmp可有可无
}
};
解法2(双指针):
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
//采用双指针来进行解题
int p1 = 0;
int p2 = 0;
int temp = 0;
int result[m+n];
while(p1<m||p2<n){
//p1,p2中两个都结束了才可以退出while循环
//接下来是先考虑单元素或者空元素的情况
if(p1==m){
//p1为单元素或者空元素
temp = nums2[p2];
p2++;
}else if(p2 == n){
//p2为单元素或者空元素
temp = nums1[p1];
p1++;
}else if(nums1[p1]<nums2[p2]){
temp = nums1[p1];
p1++;
}else {
temp = nums2[p2];
p2++;
}
result[p1+p2-1] = temp;
}
for(int i = 0;i<m+n;i++){
//将最终结果拷贝到题目要求的nums1数组上
nums1[i] = result[i];
}
}
};
解法3(后插):
观察讨论区小伙伴们的题解之后,发现由于nums1数组的特殊性,从后面开始确定结果数组会更加方便
因此题解2补充如下:
p1为nums1有数字部分的末尾,p2为nums2有数字部分的末尾,p为nums1的末尾
从p位置开始,每次将p1和p2位置的数字分别比较,大的就填入p位置当中。
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int len1= m-1;
int len2= n-1;
int len = m+n-1;
while(len1>=0&&len2>=0){
nums1[len--] = nums1[len1]>nums2[len2]?nums1[len1--]:nums2[len2--];
}
//如果没有执行while循环 则说明m和n当中有一个为0,此时无论是哪种情况,将nums2中数据拷贝到nums1中即可
if(len1==-1){
for(int i=0;i<=len2;i++){
nums1[i] = nums2[i];
}
}
}
};
本解法关键在于第11行到13行的代码,如果没有这三行,我们会发现,首先会出现
出现这个错误的原因是,如果一开始m就等于0,则len1赋初始值的时候,就为-1了,因此不会进入while循环,所以输出结果就是[0],而实际上并不是这样的,于是我立马抓住了m=0的情况,改进了一下代码:
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int len1= m-1;
int len2= n-1;
int len = m+n-1;
while(len1>=0&&len2>=0){
nums1[len--] = nums1[len1]>nums2[len2]?nums1[len1--]:nums2[len2--];
}
if(m==0){
//既然nums1为空,则nums2就是所求题解,我们直接把nums2赋值到nums1上就可以了
nums1 = nums2;
}
}
};
提交之后,又出现了新问题:
出现这种情况的原因是nums2中的数字太小了,从后面从大到小插,导致len1指针都到了nums1的头了,nums2中还有元素没能插进nums1中,到这里我们也就总结出了这个题会出现的两种情况:
第一种是len2指针首先变为-1,这个时候说明nums2中的数字都比较大,直接比较早地就插入在了nums1的后端;不过还有可能是nums2一开始就没有元素,而nums1中有元素,此时m>0,而n=0,则len2=n-1,就先于len1一步变为-1了。对于这两种情况,无需处理,此时的nums1就是我们想要的答案,
第二种是len1指针先变为-1,这个时候说明nums2中的数字比较小,没能插在nums1的后面,部分nums2中的元素需要从nums1的头部插进去,此时我们就需要再加上几行代码,题解评论中一个大佬用了java中的高级函数,本人由于弱鸡,不记得那些高级函数,就想着能不能朴素地用c++也完成这种情况的处理。通过仔细观察我们不难发现,len1先变为-1,所以我们可以采用判断语句,判断条件为len1是否等于-1,如果等于-1的话,我们需要把nums2中的元素从头到len2指针的位置搬到nums1中从头开始的位置,由于这个挪动复制动作,需要搬动的nums2中的元素有多少是与nums2密切相关的,所以我们就设计了如下代码:
if(len1==-1){
for(int i=0;i<=len2;i++){
nums1[i] = nums2[i];
}
}
len1指针先变为-1还可能是因为在一开始,nums1就为空的,就是我们第一次提交出现的那种情况,此时m=0,所以len1也就是-1了,这种情况,同样也适用于上面的代码段,可以说上面的代码段就算是原博主用java写的
/ 表示将nums2数组从下标0位置开始,拷贝到nums1数组中,从下标0位置开始,长度为len2+1
System.arraycopy(nums2, 0, nums1, 0, len2 + 1);
这一段的一个简单复现而已,只不过我没有用arraycopy这个高级函数而已。
综上算法改进,最后提交,得到AC结果如下:
执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:8.9 MB, 在所有 C++ 提交中击败了33.62%的用户
通过测试用例:59 / 59
时间复杂度为O(m+n)