本篇文章为笔者的LeetCode刷题笔记。文章整体分为两部分:1.笔者自己思考的算法及代码。2.LeetCode官方给出的最优算法及代码。通过对比这两部分算法和代码的异同,笔者的算法思想和编程水平有了显著地提升。如果这篇文章能帮到你那真是再好不过了!
一、笔者思考的解法及代码实现
A.算法:
1.双指针 ptr1, ptr2分别指向 nums1, nums2 最后一个数组元素(ptr1指向nums1第m-1个位置)
2.新malloc一个nums3数组,insert指针指向其下一个待插入位置(从最后一个位置开始插入)
3.分别将ptr1与ptr2所指的数组元素比较,大者插入insert指向的位置
4.当ptr1或ptr2有一个<0时(有一个数组已经遍历结束),将另一个数组中剩余元素依次放入nums3中
5.用nums3覆盖nums1
B.代码实现:
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
int *nums3 = malloc(sizeof(int)*(m+n));
int insert=m+n-1; //nums3数组待插入指针,初始化指向最后一个位置
int ptr1=m-1, ptr2=n-1;
for(int i=0;i<m+n;i++){ //遍历nums1数组
if(ptr1>=0 && ptr2>=0){ //条件判断 nums1和nums2都未遍历完毕时进入(调整ptr1<0和ptr2<0顺序可将此步省略)
if(nums1[ptr1]<=nums2[ptr2]){
nums3[insert]=nums2[ptr2];
ptr2--;
insert--;
}else if(nums1[ptr1]>nums2[ptr2]){
nums3[insert]=nums1[ptr1];
ptr1--;
insert--;
}
}if(ptr1<0){ //当nums1遍历结束
int leftnums = ptr2;
for(int j=0;j<leftnums+1;j++){ //把nums2剩余元素依次加入nums3
nums3[insert]=nums2[ptr2];
ptr2--;
insert--;
}
for(int k=0;k<m+n;k++){
nums1[k]=nums3[k];
}
}else if(ptr2<0){ /当nums2遍历结束
int leftnums = ptr1;
for(int j=0;j<leftnums+1;j++){ //把nums2剩余元素依次加入nums3
nums3[insert]=nums1[ptr1];
ptr1--;
insert--;
}
for(int k=0;k<m+n;k++){ //把nums3复制到nums1
nums1[k]=nums3[k];
}
}
}
}
C.复杂度分析:
时间复杂度:O(m+n)
指针移动单调递减,最多移动m+n次
空间复杂度:O(m+n)
需要建立长度为m+n的中间数组nums3
D.算法缺点:1.没有利用nums1多余的n个空闲位,额外创建了nums3,增加了O(m+n)空间复杂度
二、官方最优解法及代码实现
A.算法:
在笔者算法的基础上,观察可知nums1的后半部分是空的,可以直接覆盖并且不会影响结果。因此可以指针设置为从后向前遍历,每次取两者之中的较大者放进nums1的最后面。
B.代码实现:
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
int insert=m+n-1;
int ptr1=m-1, ptr2=n-1;
for(int i=0;i<m+n;i++){ //从后向前遍历nums1,也可换成while(ptr1>=0 || ptr2>=0) 条件循环)
if(ptr1<0){ //将此句放在前面可以少些一个if判断,笔者自己的代码有提到
nums1[insert--]=nums2[ptr2--];
}else if(ptr2<0){ //将此句放在前面可以少些一个if判断,笔者自己的代码有提到
nums1[insert--]=nums1[ptr1--];
}else if(nums1[ptr1]<=nums2[ptr2]){
nums1[insert--]=nums2[ptr2--]; //注意理解insert--和ptr2--的作用
}else if(nums1[ptr1]>nums2[ptr2]){
nums1[insert--]=nums1[ptr1--];
}
}
}
C.复杂度分析:
时间复杂度:O(m+n)
指针移动单调递减,最多移动m+n次
空间复杂度:O(1)
直接对nums1修改,没有用到新数组,不需要额外空间
三、笔者小结,二者对比
A.算法对比:
1.想到了双指针尾插法,但忽略了nums1有多余的n个位置未利用
B.代码对比:
1.用for(int i=0;i<m+n;i++)和while(ptr1>=0 || ptr2>=0)条件循环皆可
2.注意理解nums3[insert]=nums1[ptr1];
ptr1--;
insert--;
和nums1[insert--]=nums1[ptr1--];之间的差距,可以使代码更简洁高效
3.四种情况:ptr1<0, ptr2<0, nums1[ptr1]<=nums2[ptr2], nums1[ptr1]>nums2[ptr2]
这四个条件判断的先后位置很重要,ptr1<0, ptr2<0 应该放在前面。
Keep going and carry on!
谢谢你看到这里!!!