[菜鸟训练]53. 最大子序和

本文介绍了如何使用动态规划和分治算法解决寻找整数数组中具有最大和的连续子数组的问题。动态规划方法通过维护一个dp数组,找到每个位置的最大子数组和,最终得到全局最大值。而分治算法虽然复杂度较高,但能进一步获取任意区间内的最大子段和。示例代码展示了这两种方法的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

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

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

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

示例 3:
输入:nums = [0]
输出:0

示例 4:
输入:nums = [-1]
输出:-1

示例 5:
输入:nums = [-100000]
输出:-100000

提示:
1 <= nums.length <= 3 * 104
-105 <= nums[i] <= 105

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

解题思路:

方法一: dp,根据题目我们不难得出表达式:
dp[i] = max(dp[i-1]+nums[i],nums[i]);

dp[i]代表当前前i个元素的最大和是多少。

代码:

public class LC53 {
    public int maxSubArray(int[] nums) {
        //dp问题
        int[] dp = new int[nums.length + 10];
        dp[0] = nums[0];
        int max = nums[0];
        for (int i = 1; i < nums.length; i++){
            dp[i] = Math.max(dp[i-1] + nums[i], nums[i]);
            max = Math.max(dp[i],max);
        }
        return max;

    }

    public static void main(String[] args){
        LC53 obj = new LC53();
        int[] nums = new int[]{2,1,-3,4,-1,2,1,-5,4};
        System.out.println(obj.maxSubArray(nums));
    }
}

方法二: 分治算法,时间复杂度 O(n) 空间复杂度O(logn)。不如dp简单且高效,但该方法可以进一步得出任意区间内的最大子段和。个人还是建议使用dp。

	/*
    分治算法  时间复杂度 O(n)  空间复杂度O(logn)
    对于一个区间[l,r]我们维护该区间的四个量:
    lSum:表示[l,r]内以l为左端点的最大子段和
    rSum:表示[l,r]内以r为右端点的最大子段和
    iSum:表示[l,r]的区间和
    mSum:表示[l,r]内的最大子段和


    好处:我们通过此方法可以进一步得出任意区间上的最大子段和
     */
    class Status{
        public int lSum,rSum,mSum,iSum;

        public Status(int lSum, int rSum, int mSum, int iSum) {
            this.lSum = lSum;
            this.rSum = rSum;
            this.mSum = mSum;
            this.iSum = iSum;
        }
    }

    public int maxSubArray(int[] nums) {
        return getmSum(nums, 0, nums.length - 1).mSum;
    }

    //分治,一层层向下分,直到区间元素只有一个时,再往上返
    public Status getmSum(int[] nums, int l, int r){
        //如果该区间内仅有一个数,那四个值都为该数本身
        if (l == r){
            return new Status(nums[l], nums[l], nums[l], nums[l]);
        }
        int mid = (l + r)/2;
        Status lSub = getmSum(nums,l,mid);
        Status rSub = getmSum(nums,mid+1,r);
        return pushUp(lSub,rSub);
    }

    /*
    判断当前区间中的值为多少:以下说的左区间为[l,mid] 右区间为[mid+1,r] 总区间为[l,r]
    iSum:总区间的iSum就是左右区间的iSum相加
    lSum:总区间的iSum可能是左区间的iSum,也可能是左区间的iSum+右区间的lSum,找两者的最大值
    rSum:总区间的rSum可能是右区间的rSum,也可能是右区间的iSum+左区间的rSum,找两者的最大值
    mSum:总区间的mSum可能包括了mid,也可能不包括,所以找左区间mSum、右区间mSum和左区间rSum+右区间lSum的最大值
     */
    public Status pushUp(Status l,Status r){
        int iSum = l.iSum + r.iSum;
        int lSum = Math.max(l.lSum, l.iSum + r.lSum);
        int rSum = Math.max(r.rSum, l.rSum + r.iSum);
        int mSum = Math.max(Math.max(l.mSum,r.mSum), l.rSum + r.lSum);
        return new Status(lSum,rSum,mSum,iSum);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值