在这篇博客中,我们将讨论一个常见的编程问题:如何合并两个按非递减顺序排列的整数数组,使得合并后的数组仍然按非递减顺序排列。我们将通过一个具体的例子和 C++ 的实现来帮助大家理解这个过程。
问题描述
给定两个整数数组 nums1
和 nums2
,以及两个整数 m
和 n
,其中 m
表示 nums1
中的有效元素数量,而 n
表示 nums2
中的元素数量。我们的目标是将 nums2
合并到 nums1
中,使得合并后的数组仍然保持非递减顺序。
输入输出示例
示例 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]
解决思路
- 我们可以将两个数组合并成一个,然后对其进行排序,
stl
的sort
方法直接排完序之后返回,也可以使用快速排序对其进行排序返回(快速排序的平均时间复杂度为 O ( n log n ) O(n \log n) O(nlogn),递归深度为 log n \log n logn,每次操作需要的线性时间为 O ( n ) O(n) O(n)) - 双指针法来解决这个问题。由于
nums1
的末尾包含足够的空间来容纳nums2
中的所有元素,因此我们可以从后向前填充nums1
,这么做的时间复杂度是 O ( m + n ) O(m+n) O(m+n),空间复杂度是 O ( 1 ) O(1) O(1),因为我们最多循环m + n 次,由于没有占用额外的空间,所以时间复杂度是常数
具体步骤
排序
排序的方式很简单,先对两个数组合并然后直接sort就行了,我这里没有用这个办法,太简单了不想写。
双指针
- 定义两个指针
m
和n
,分别指向nums1
和nums2
的有效元素的最后一个索引。 - 定义一个指针
tail
,指向合并后数组的最后一个位置。 - 比较
nums1[m-1]
和nums2[n-1]
,将较大的元素放到nums1[tail]
中,并移动相应的指针。 - 如果
nums2
中的元素未完全处理完,则继续将剩余的元素填入nums1
。 - 最终,
nums1
将包含合并后的有序数组。
c++
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int tail = m + n - 1; // 合并后数组的最后一个位置
while (n > 0 && m > 0) {
// 从后向前比较并填充
nums1[tail--] = nums1[m - 1] > nums2[n - 1] ? nums1[--m] : nums2[--n];
}
// 如果上面的循环循环完了,m中的元素可能已经全部取出,但n的元素不一定全部取出
while (n > 0) {
nums1[tail--] = nums2[--n];
}
}
};
代码解析
tail
初始化为m + n - 1
,指向合并后数组的最后一个位置。- 在
while
循环中,我们比较nums1
和nums2
中的元素,并将较大的元素放到nums1[tail]
。这确保了合并后的数组保持非递减顺序。 - 当
nums2
中还有元素未处理完时,我们需要将它们填入nums1
。
时间复杂度和空间复杂度
- 时间复杂度:O(m + n),因为我们需要遍历两个数组的所有元素。
- 空间复杂度:O(1),由于我们在原地进行合并,不需要额外的空间。
结论
通过上述方法,我们成功地将两个有序数组合并为一个新的有序数组。希望这篇博客能帮助你理解合并有序数组的过程和相关的 C++ 实现