常见算法与数据结构六(合并两个有序数组、子数组最大平均数)

目录

合并两个有序数组

合并后排序

双指针

子数组最大平均数


两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。

合并两个有序数组

合并后排序

public void merge (int[] nums1, int m, int[] nums2, int n) {
        System.arraycopy(nums2, 0, nums1, m, n);
        Arrays.sort(nums1);
    }

该函数的详细分析如下:

  1. System.arraycopy 方法被用来将 nums2 数组中的所有元素复制到 nums1 数组中从下标 m 开始的位置。这个操作假设 nums1 数组有足够的空间来容纳所有来自 nums2 的元素。也就是说,nums1 的长度至少应该是 m + n

  2. Arrays.sort(nums1) 调用是用来对合并后的数组进行排序。由于 nums1 和 nums2 原本就是有序的,这种方法虽然能够得到正确结果,但是并不是最高效的。实际上,这个排序步骤的时间复杂度是 O((m+n)log(m+n)),这不是合并两个有序数组最优的时间复杂度。

双指针

一个更高效的方法是使用双指针技术,从两个数组的末尾开始,将较大的元素依次放入 nums1 的末尾。这样,可以保持数组的有序性,而不需要进行全局排序。这种方法的时间复杂度是 O(m+n),因为每个元素只需要比较和移动一次。

public void merge(int[] nums1, int m, int[] nums2, int n) {
    // 初始化两个指针的位置在两个数组的末尾
    int i = m - 1;
    int j = n - 1;
    // 初始化另一个指针在合并后的数组的末尾
    int k = m + n - 1;

    // 当两个数组都还有元素时
    while (i >= 0 && j >= 0) {
        // 将较大的元素复制到 nums1 的末尾
        if (nums1[i] > nums2[j]) {
            nums1[k] = nums1[i];
            i--;
        } else {
            nums1[k] = nums2[j];
            j--;
        }
        k--;
    }

    // 如果 nums2 中还有元素未复制完,将其余的元素复制到 nums1 中
    while (j >= 0) {
        nums1[k] = nums2[j];
        j--;
        k--;
    }

    // 如果 nums1 中还有元素未处理完,它们已经在正确的位置上,不需要再移动
}

不再使用 Arrays.sort 方法,而是通过比较和移动,直接将元素放置在合适的位置上。

子数组最大平均数

给一个整数数组,找出平均数最大且长度为 k 的下标连续的子数组,并输出该最大平均数。

public double findMaxAverage(int[] nums, int k) {
        int sum = 0;
        int n = nums.length;
        for (int i = 0; i < k; i++) {
            sum += nums[i];
        }
        int maxSum = sum;
        for (int i = k; i < n; i++) {
            sum = sum - nums[i - k] + nums[i];
            maxSum = Math.max(maxSum, sum);
        }
        return 1.0 * maxSum / k;
    }

该函数的详细分析如下:

  1. 初始化变量 sum 为 0,用来存储当前考虑的子数组的元素和。

  2. n 是数组 nums 的长度。

  3. 第一个 for 循环遍历数组 nums 的前 k 个元素,并计算这些元素的和,存储在 sum 中。

  4. maxSum 初始化为 sum,用来追踪遇到的最大子数组和。

  5. 第二个 for 循环从数组的第 k 个元素开始,到数组的末尾。在每一步中,更新 sum 为考虑新的连续子数组的和。这是通过从 sum 中减去滑动窗口最左边的元素 nums[i - k] 并加上最右边的新元素 nums[i] 来完成的。

  6. 在每次更新 sum 之后,通过 Math.max(maxSum, sum) 更新 maxSum 为最大值。

  7. 循环结束后,我们得到了所有长度为 k 的连续子数组中的最大和。

  8. 最后,函数返回 maxSum / k 的结果,这是最大子数组和的平均值。

  9. 由于 maxSum 和 k 都是整数,因此使用 1.0 * maxSum / k 来确保结果是 double 类型的。

其实这个函数实现了一个称为“滑动窗口”的算法,它以 O(n) 的时间复杂度有效地找到了最大平均值的子数组。

这种方法避免了对每个可能的子数组重新计算和的低效操作,而是利用了连续子数组之间的重叠。

通过在滑动窗口中添加一个新元素的同时移除一个旧元素,可以快速更新子数组的和。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值