合并有序数组
以下是几种合并有序数组的方法:
1. 顺序双指针法(使用额外数组)
public void mergeSortedArrays(int[] nums1, int m, int[] nums2, int n) {
int[] result = new int[m + n];
int i = 0, j = 0, k = 0;
// 遍历两个数组,将较小的元素放入result数组
while (i < m && j < n) {
if (nums1[i] <= nums2[j]) {
result[k++] = nums1[i++];
} else {
result[k++] = nums2[j++];
}
}
// 将剩余的元素复制到result数组
while (i < m) {
result[k++] = nums1[i++];
}
while (j < n) {
result[k++] = nums2[j++];
}
// 将result数组的内容复制回nums1
System.arraycopy(result, 0, nums1, 0, m + n);
}
2. 直接合并后排序
import java.util.Arrays;
public void mergeAndSort(int[] nums1, int m, int[] nums2, int n) {
// 将nums2的元素复制到nums1的末尾
for (int i = 0; i < n; i++) {
nums1[m + i] = nums2[i];
}
// 对整个nums1数组进行排序
Arrays.sort(nums1);
// 注意:这种方法实际上改变了nums1的原始顺序,如果要求保持nums1前m个元素的相对顺序,则不适用
}
// 注意:由于排序会改变整个数组的顺序,这通常不是题目要求的解决方案
// 除非题目允许或明确说明可以这样做
3. 元素插入法(实际上更接近于逆向双指针法的变种)
由于直接从前往后插入元素会涉及到多次移动,这里提供一个更接近于逆向双指针法但稍有不同的思路,即仍然从后往前遍历,但考虑到直接操作nums1
:
public void mergeSortedArraysByInsert(int[] nums1, int m, int[] nums2, int n) {
int i = m - 1, j = n - 1, k = m + n - 1;
// 从后往前遍历,将较大的元素放入nums1的末尾
while (i >= 0 && j >= 0) {
if (nums1[i] >= nums2[j]) {
nums1[k--] = nums1[i--];
} else {
nums1[k--] = nums2[j--];
}
}
// 如果nums2还有剩余元素,将它们依次放到nums1的前面
// 但由于我们是从后往前遍历的,这一步其实已经隐含在上面的循环中了
// 如果nums1还有剩余元素(即i >= 0),那么它们已经是有序的,并且位于nums1的前面,无需处理
}
// 注意:上面的代码其实就是逆向双指针法的实现,只是解释的角度略有不同
// 真正的“元素插入法”在这里并不适用,因为它会导致多次不必要的元素移动
在实际应用中,通常会选择逆向双指针法,因为它既高效又不需要额外的空间(除了几个指针变量)。其他方法要么需要额外的空间,要么效率较低。
原理解析
1. 顺序双指针法(使用额外数组)
这种方法使用两个指针分别指向两个输入数组,并维护一个额外的数组来存储合并后的结果。最后,将结果数组的内容复制回原数组 nums1
。
步骤:
- 初始化三个指针:
i
指向nums1
的开始,j
指向nums2
的开始,k
指向结果数组result
的开始。 - 遍历两个输入数组,比较
nums1[i]
和nums2[j]
的大小,将较小的元素放入result[k]
,并移动对应的指针。 - 如果某个数组遍历完,将另一个数组剩余的元素直接复制到
result
的末尾。 - 将
result
数组的内容复制回nums1
。
优点:逻辑清晰,易于实现。
缺点:需要额外的空间来存储结果数组。
2. 直接合并后排序
这种方法首先将 nums2
的所有元素复制到 nums1
的末尾(即覆盖原有的 0
),然后对整个 nums1
数组进行排序。
步骤:
- 将
nums2
的元素复制到nums1
的末尾(从索引m
开始)。 - 对整个
nums1
数组进行排序。
优点:实现简单,代码量少。
缺点:排序的时间复杂度较高,通常为 O((m+n)log(m+n)),且需要额外的空间(取决于排序算法的实现)。
3. 元素插入法
这种方法类似于在已排序数组中插入元素的过程,但考虑到 nums1
的后半部分是空的,可以直接从后往前比较和插入。
步骤:
- 初始化两个指针:
i
指向nums1
的末尾(即m+n-1
),j
指向nums2
的末尾(即n-1
)。 - 从后往前遍历
nums1
和nums2
,比较当前元素的大小,将较大的元素放入nums1[i]
并递减i
。 - 如果
nums2
还有剩余元素,将它们依次放到nums1
的前面。
注意:这种方法在实际应用中可能不太常见,因为它需要多次移动 nums1
中的元素,效率不如逆向双指针法。
总结
在合并两个有序数组的场景中,逆向双指针法是最优的选择,因为它既不需要额外的空间(除了几个指针变量),又能达到 O(m+n) 的时间复杂度。如果空间复杂度不是主要考虑因素,使用额外数组的双指针法或直接合并后排序的方法也是可行的。然而,在追求高效和节省空间的场景下,逆向双指针法通常是首选。