题目
我的解答思路
首先想到的是归并排序,恰好两路归并排序算法是稳定的算法,决定套模板(3w)用空间换时间
我新开了一个数组nums3,然后之后再把nums3复制给nums1。然后因为m+n限制在200以内所以我的nums3开的210的大小
cpp
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = 0, j = 0, k = 0;
int nums3[210];
while (i < m && j < n) {
if (nums1[i] <= nums2[j])
nums3[k++] = nums1[i++];
else
nums3[k++] = nums2[j++];
}
while (i < m)
nums3[k++] = nums1[i++];
while (j < n)
nums3[k++] = nums2[j++];
for(int i=0;i<k;i++){
nums1[i]=nums3[i];
}
}
};
也是顺利通过了!
复杂度分析
时间复杂度:O(m+n)
空间复杂度:O(m+n)
另优解:从后向前的双指针法(来自gimini老师的教学)
直接在 nums1 数组上操作,但通过从末尾开始填充,完美地避免了覆盖 nums1 中尚未被比较的原始数据。
指针定义
我们需要三个指针:
p1:指向 nums1 中最后一个有效元素。初始位置为 m - 1。
p2:指向 nums2 中最后一个元素。初始位置为 n - 1。
p_merge:指向 nums1 数组的最末尾,这是我们将要填充的位置。初始位置为 m + n - 1。
Step1:初始化指针
cpp
int p1 = m-1;
int p2 = n-1;
int p_merge = m+n-1;
Step2 从后向前比较和填充
进入一个 while 循环,循环条件是 p2 >= 0。为什么是 p2?因为只要 nums2 还没合并完,我们就需要继续操作。如果 nums2 合并完了,而 nums1 还有剩余(p1 >= 0),那么这些 nums1 的元素本身就在正确的位置上,不需要移动。
在循环内部,进行比较:
情况一:p1 还在界内,并且 nums1[p1] 大于 nums2[p2]
说明当前两个数组的末尾元素中,nums1 的更大。
将这个较大的值 nums1[p1] 放入 nums1 的 p_merge 位置。
cpp
nums1[p_merge] = nums1[p1];
移动 p1 和 p_merge 指针,去比较下一个位置。
cpp
p1--;
p_merge--;
情况二:其他情况
这包含了两种子情况:
p1 还在界内,但 nums1[p1] <= nums2[p2]。
p1 已经越界了(p1 < 0),说明 nums1 的所有有效元素都已经合并完毕,现在只需要把 nums2 中剩余的元素逐个放入 nums1 的前端即可。
在这两种子情况下,我们都应该将 nums2[p2] 的值放入 nums1[p_merge] 的位置。
cpp
nums1[p_merge] = nums2[p2];
移动 p2 和 p_merge 指针。
cpp
p2--;
p_merge--;
Step3 循环结束
当 p2 变为 -1 时,循环终止。此时,所有 nums2 中的元素都已经被正确地合并到了 nums1 中,整个 nums1 数组 (0 到 m+n-1) 就变得有序了。
完整代码实现
cpp
class Solution {
public:
/**
* @brief 使用从后向前的双指针法合并两个有序数组
* @param nums1 目标数组,长度为 m+n,后 n 个元素为0
* @param m nums1 中的有效元素个数
* @param nums2 要合并的数组,长度为 n
* @param n nums2 中的元素个数
*/
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
// 初始化指针:p1 指向 nums1 的有效元素末尾
int p1 = m - 1;
// p2 指向 nums2 的末尾
int p2 = n - 1;
// p_merge 指向 nums1 的最末尾,用于放置最终结果
int p_merge = m + n - 1;
// 只要 nums2 还没有被完全合并,就继续循环
while (p2 >= 0) {
// 如果 p1 还在界内,并且 p1 指向的元素大于 p2 指向的元素
if (p1 >= 0 && nums1[p1] > nums2[p2]) {
// 将 nums1[p1] 这个较大的值放到 nums1 的末尾
nums1[p_merge] = nums1[p1];
// 移动 p1 指针,向前一位
p1--;
} else {
// 发生以下两种情况之一:
// 1. p1 越界了 (m=0 的情况)
// 2. nums2[p2] 的值大于或等于 nums1[p1] 的值
// 无论哪种情况,都应将 nums2[p2] 的值放到 nums1 的当前合并位置
nums1[p_merge] = nums2[p2];
// 移动 p2 指针,向前一位
p2--;
}
// 每次放置一个元素后,合并指针 p_merge 都需要向前移动
p_merge--;
}
// 当 p2 < 0 时,循环结束。
// 无需处理 p1 > 0 的情况,因为那些元素已经在正确的位置上了。
}
};
也是顺利通过了,感谢gimini老师
时间复杂度:O(m+n)
空间复杂度:O(1)
ok see you next time~