算法题:合并有序数组

合并有序数组

以下是几种合并有序数组的方法:

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)。
  • 从后往前遍历 nums1nums2,比较当前元素的大小,将较大的元素放入 nums1[i] 并递减 i
  • 如果 nums2 还有剩余元素,将它们依次放到 nums1 的前面。

注意:这种方法在实际应用中可能不太常见,因为它需要多次移动 nums1 中的元素,效率不如逆向双指针法。

总结

在合并两个有序数组的场景中,逆向双指针法是最优的选择,因为它既不需要额外的空间(除了几个指针变量),又能达到 O(m+n) 的时间复杂度。如果空间复杂度不是主要考虑因素,使用额外数组的双指针法或直接合并后排序的方法也是可行的。然而,在追求高效和节省空间的场景下,逆向双指针法通常是首选。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

望佑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值