洋钱罐高频编程考题:最大子数组和 (中等)

题目描述

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例 1:输入:nums = [-2,1,-3,4,-1,2,1,-5,4]   输出:6   解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:输入:nums = [1]   输出:1

示例 3:输入:nums = [5,4,-1,7,8]   输出:23

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104

进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。

解题思路

这个问题可以通过动态规划分治法两种方法解决。

动态规划法:动态规划法的核心思想是使用一个变量 currentMax 来记录当前子数组的最大和,然后更新全局最大和 globalMax

具体步骤

  1. 初始化 globalMaxcurrentMax 为数组的第一个元素。
  2. 从数组的第二个元素开始,遍历每个元素:更新 currentMaxMath.max(currentElement, currentMax + currentElement)。即决定是继续扩展当前子数组,还是从当前元素重新开始子数组;更新 globalMaxMath.max(globalMax, currentMax)
  3. 返回 globalMax 作为最终结果。

这种方法的时间复杂度为 O(n),空间复杂度为 O(1)。

分治法:分治法利用递归将数组分为左右两部分,并结合三个部分来找到最大和:

  1. 左半部分的最大子数组和。
  2. 右半部分的最大子数组和。
  3. 跨越中间的最大子数组和。

具体步骤

  1. 将数组分成左右两部分。
  2. 递归地计算左半部分和右半部分的最大子数组和。
  3. 计算跨越中间的最大子数组和。首先从中间向左扩展,找到左侧最大和,然后从中间向右扩展,找到右侧最大和,最后求和得到跨越中间的最大子数组和。
  4. 返回三个值中的最大值。

这种方法的时间复杂度为 O(n log n),空间复杂度为 O(log n)。

复杂度分析

动态规划法

  • 时间复杂度:O(n)。我们遍历一次数组,每个元素进行常数时间的操作。
  • 空间复杂度:O(1)。我们只使用了常数空间来存储变量。

分治法

  • 时间复杂度:O(n log n)。每次分割数组都需要 O(n) 时间来计算跨越中间的子数组和,并且总共有 O(log n) 层递归。

  • 空间复杂度:O(log n)。递归调用栈的深度为 O(log n)。

代码实现

package org.zyf.javabasic.letcode.hot100.ordinaryarray;

/**
 * @program: zyfboot-javabasic
 * @description: 最大子数组和
 * @author: zhangyanfeng
 * @create: 2024-08-21 22:04
 **/
public class MaxSubArraySolution {
    public int maxSubArray1(int[] nums) {
        if (nums == null || nums.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty");
        }

        // 初始化动态规划变量
        int globalMax = nums[0];
        int currentMax = nums[0];

        for (int i = 1; i < nums.length; i++) {
            currentMax = Math.max(nums[i], currentMax + nums[i]);
            globalMax = Math.max(globalMax, currentMax);
        }

        return globalMax;
    }

    public int maxSubArray2(int[] nums) {
        if (nums == null || nums.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty");
        }

        return maxSubArray(nums, 0, nums.length - 1);
    }

    private int maxSubArray(int[] nums, int left, int right) {
        if (left == right) {
            return nums[left];
        }

        int mid = (left + right) / 2;

        // 计算左半部分、右半部分以及跨越中间的最大子数组和
        int leftMax = maxSubArray(nums, left, mid);
        int rightMax = maxSubArray(nums, mid + 1, right);
        int crossMax = maxCrossingSubArray(nums, left, mid, right);

        // 返回三个部分中的最大值
        return Math.max(Math.max(leftMax, rightMax), crossMax);
    }

    private int maxCrossingSubArray(int[] nums, int left, int mid, int right) {
        int leftSum = Integer.MIN_VALUE;
        int sum = 0;

        // 计算跨越中间的最大子数组和(从中间向左)
        for (int i = mid; i >= left; i--) {
            sum += nums[i];
            if (sum > leftSum) {
                leftSum = sum;
            }
        }

        int rightSum = Integer.MIN_VALUE;
        sum = 0;

        // 计算跨越中间的最大子数组和(从中间向右)
        for (int i = mid + 1; i <= right; i++) {
            sum += nums[i];
            if (sum > rightSum) {
                rightSum = sum;
            }
        }

        // 返回跨越中间的最大子数组和
        return leftSum + rightSum;
    }

    public static void main(String[] args) {
        MaxSubArraySolution solution = new MaxSubArraySolution();

        // 测试用例 1
        int[] nums1 = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
        System.out.println(solution.maxSubArray1(nums1));  // 输出: 6

        // 测试用例 2
        int[] nums2 = {1};
        System.out.println(solution.maxSubArray1(nums2));  // 输出: 1

        // 测试用例 3
        int[] nums3 = {5, 4, -1, 7, 8};
        System.out.println(solution.maxSubArray1(nums3));  // 输出: 23
    }
}

具体可参考:https://zyfcodes.blog.csdn.net/article/details/141401712

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值