Java-最大子数组和问题

文章目录

  • 前言
  • 一、求最大子数组的和问题
  • 二、问题分析
  • 三、总结


前言

算法分析:分治法


一、求最大子数组的和问题

给你一个整数数组 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) 的解法,尝试使用更为精妙的 分治法 求解。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/maximum-subarray
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、问题分析

提示:在这里我们只对分治法进行描述,本体可以使用动态规划法完成

我们定义一个操作 getInfo(a, l, r) 表示查询 a 序列 [l,r] 区间内的最大子段和,那么最终我们要求的答案就是 getInfo(nums, 0, nums.length() - 1)。
如何分治实现这个操作呢?
对于一个区间 [l,r],我们取 m = (l+r)/2,对区间 [l,m]和 [m+1,r]分治求解。当递归逐层深入直到区间长度缩小为1的时候,递归「开始回升」。

	public static MaxSubArray getInfo(int[] a, int l, int r) {
		if (l == r) {// 递归出口
			return new MaxSubArray(a[l], a[l], a[l], a[l]);
		}
		// 递归函数
		int m = (l + r) / 2;
		MaxSubArray lSub = getInfo(a, l, m);// 左子区间
		MaxSubArray rSub = getInfo(a, m + 1, r);// 右子区间
		return pushUp(lSub, rSub);
	}

这个时候我们考虑如何通过 [l,m]区间的信息和 [m+1,r]区间的信息合并成区间 [l,r]的信息。核心点就在于:

  • 我们需要哪些信息
  • 分治法最后应该如何归并这些信息

显然,在区间[l,r]中,我们需要汇集以下信息:

  • mSum:区间[l,r]中的最大子数组和,也就是最后返回的结果
  • lSum: 区间[l,r]内以 l 为左端点的最大子段和
  • rSum:区间[l,r]内以 r 为右端点的最大子段和
  • iSum:区间[l,r]的区间和

寻找边界条件(递归出口):不难发现,当序列a长度为1时,mSum=lSum=rSum=iSum=a[i]

序列a长度大于1时,在区间 [l,r] 中:

提示:[l,m]是区间[l,r]的左子区间,[m+1,r]是区间[l,r]的右子区间

  • iSum =「左子区间」的 iSum 加上「右子区间」的 iSu

    iSum = l.iSum+r.iSum

  • lSum = 「左子区间」的 lSum 或者 lSum =「左子区间」的 iSum +「右子区间」的 lSum

    lSum = Math.max(l.lSum, l.iSum + r.lSum)

  • rSum =「右子区间」的 rSum 或者 rSum = 右子区间」的 iSum 加上「左子区间」的 rSum

    lSum = Math.max(l.lSum, l.iSum + r.lSum)

  • 最后,我们可以考虑 mSum 对应的区间是否跨越 m:

    1. 它可能不跨越 m,也就是说 mSum 可能是「左子区间」的 mSum 和 「右子区间」的 mSum 中的一个;

    2. 它也可能跨越 m,可能是「左子区间」的 rSum 和 「右子区间」的 lSum 求和。

      mSum = Math.max(Math.max(l.mSum,r.mSum),l.rSum+r.lSum)

	public static MaxSubArray pushUp(MaxSubArray l, MaxSubArray r) {
		int iSum = l.iSum + r.iSum;
		int lSum = Math.max(l.lSum, l.iSum + r.lSum);
		int rSum = Math.max(r.rSum, r.iSum + l.rSum);
		int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);// 三者取最大
		// 返回结果
		return new MaxSubArray(lSum, rSum, mSum, iSum);

	}

三、总结

这个分治方法类似于「线段树求解最长公共上升子序列问题」的 pushUp 操作。
可以借助着方法进行理解记忆

代码及运行结果:

package day1;

import java.util.Scanner;

//最大子数组和
public class MaxSubArray {
	// lSum 表示 [l,r]内以 l 为左端点的最大子段和
	// rSum 表示 [l,r]内以 r 为右端点的最大子段和
	// mSum 表示 [l,r]内的最大子段和,最终解
	// iSum 表示 [l,r]的区间和
	public int lSum, rSum, mSum, iSum;

	// 有参构造器,更新值
	public MaxSubArray(int lSum, int rSum, int mSum, int iSum) {
		this.lSum = lSum;
		this.rSum = rSum;
		this.mSum = mSum;
		this.iSum = iSum;
	}

	// 定义入口,调用getInfo()
	public static int maxSubArray(int[] nums) {
		return getInfo(nums, 0, nums.length - 1).mSum;
	}

	public static MaxSubArray getInfo(int[] a, int l, int r) {
		if (l == r) {// 递归出口
			return new MaxSubArray(a[l], a[l], a[l], a[l]);
		}
		// 递归函数
		int m = (l + r) / 2;
		MaxSubArray lSub = getInfo(a, l, m);// 左子区间
		MaxSubArray rSub = getInfo(a, m + 1, r);// 右子区间
		return pushUp(lSub, rSub);
	}

	public static MaxSubArray pushUp(MaxSubArray l, MaxSubArray r) {
		int iSum = l.iSum + r.iSum;
		int lSum = Math.max(l.lSum, l.iSum + r.lSum);
		int rSum = Math.max(r.rSum, r.iSum + l.rSum);
		int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);// 三者取最大
		// 返回结果
		return new MaxSubArray(lSum, rSum, mSum, iSum);

	}

	public static void main(String[] args) {

//		输入一个数组
		System.out.println("输入一个数组");
		Scanner scanner = new Scanner(System.in);
		String str = scanner.next().toString();
		String[] s = str.split(",");
		int[] a = new int[s.length];
		for (int i = 0; i < a.length; i++) {
			a[i] = Integer.parseInt(s[i]);
		}
		// 输出结果
		int r = maxSubArray(a);
		System.out.println(r);
		scanner.close();
	}

}

Java-最大子数组和问题_第1张图片

你可能感兴趣的:(分治法,java,算法,leetcode)