子数组最大和的变化

参考:http://mp.weixin.qq.com/s?__biz=MjM5ODIzNDQ3Mw%3D%3D&idx=1&mid=2649965753&scene=0&sn=787eab6fbd1f47563dd9bf4851499e79

原题

给定一个数组,我们可以找到两个不相交的、并且是连续的子数组A和B,A中的数字和为sum(A), B中的元素和为sum(B)。找到这样的A和B,满足sum(A) - sum(B)的绝对值是最大的。

例如:[2, -1 -2, 1, -4, 2, 8]划分为A=[-1, -2, 1, -4], B=[2, 8], 最大的值为16

【先看数组最大子段和问题,上述问题就是最大子段和的变形】

代码

 1 /**
 2  * 求数组的最大子段和
 3  * */
 4 public static int maxSubArray(int[] nums){
 5     if(nums.length==0) return 0;
 6     if(nums.length==1) return nums[0];
 7     int localMax=nums[0];
 8     int globalMax=nums[0];
 9     for(int i=1; i<nums.length; i++){
10         localMax=Math.max(localMax+nums[i], nums[i]);
11         globalMax=Math.max(globalMax, localMax);
12     }
13     return globalMax;
14 }

分析

如果没有丰富的经验,这个题目咋一看,有一种不明觉厉的感觉。但只要逐层分析,就可以看到只要分析两层就可以了。首先我们来看看题目有哪些要点:

  • 子数组是不相交的
  • 子数组是连续的,这个有点多余,但还是强调一下得好。这里并不是说,A和B是紧邻的。

然后题目的要求是,差的绝对值最大。那我们自然而然能够想到:找到的两个不相交的子数组,一个值要很小,一个值要很大。这样才能够保证差的绝对值最大。那如何找到这样的数组呢?我们从不相交的这个条件入手,这个条件很关键。看题目中例子:

0 1 2 3 4 5 6
2 -1 -2 1 -4 2 8

看上面的表格,如果两个子数组不想交,我们有六个位置,作为划分的备选,0和1之间、1和2之间、2和3之间,直到5和6之间。这六个位置,都可以将数组划分为两部分。我们设定,数组长度为n,i将数据划分为两部分分别为 [0,i-1]和[i,n-1]。都是两边包含的集合。i是从1到n-1的。

对于任意的i,我们得到了两部分[0, i-1]和[i, n-1]。接下来,就是在这两部分中,找到一个和最小的子数组A,以及和最大的子数组B。那么A-B的绝对值,就是i这个划分下,满足条件的两个数组的差的最大值。对于,所有的i而言,这个绝对值最大时的A和B就是我们要找到的。如果能找到每一个划分的最大值,最小值,则遍历一边就得到结果,O(n)搞定。那最大值最小值可以O(n)搞定么?

思路更进一步,接下来要确定,找到在i处划分,和最大以及和最小的子数组的方法。这里,就要使用到我们之前分享的一个题目的思路。那篇文章,大家好好阅读分析了么。相信一定能够给大家带来很多的启发。回到这个题目,我们考虑,给定一个数组,求和最大的子数组以及和最小的子数组。

先分析和最大的子数组,这个问题,是比较经典的问题了,但是我们这里要处理的是,求得每一个i左侧的最大连续子数组。作如下分析,假设数组为X, 假设max_until[i]表示,以i位置结尾的连续子数组的最大和。max_until[i]和max_until[i-1]是什么关系呢?

  • 如果X[i] + max_until[i - 1] > max_until[i - 1] and X[i] + max_until[i - 1] > X[i]。那么X[i]应该加入到连续子数组中,max_until[i] = max_until[i-1] + X[i].
  • 否则max_until[i] = X[i],连续子数组只有一个元素。

但是,我们要的并不是以i结尾的子数组,尽管给的例子中是这样的,我们要的是i之前的所有连续子数组中,和最大的。并不一定包括i。要如何处理呢?我们再开辟子数组max_left[i]表示[0,i]中连续子数组的最大值。那这个值要如何求得呢?我们在遍历数组,求得max_until[i]的时候,max_left[i]只需要在max_until[i]和此前保存的最大值里取最大的即可。也就是一次遍历,就可以完全求得max_until数组和max_left数组。同理可以求得min_until以及min_left数组。其实在求最大值的时候,并不需要max_left。那么这里为什么还要开辟空间呢?后面有答案。

这是处理的划分的左半部分。那么右半部分呢?

右半部分的思路也是一样的,只不过,我们在遍历数组的时候,需要从右向左进行遍历。

总结整个方法的流程如下:

  • 从左向右遍历数组,计算max_left和min_left数组,O(n)时间复杂度
  • 从右向左遍历数组,计算max_right和min_right数组,O(n)时间复杂度
  • 然后对于每一个i,i从1开始到n-1,计算max_left[i - 1] - min_right[i], max_right[i] - min_left[i - 1]。选取绝对值最大的。

方法的整体空间复杂度为O(n),时间复杂度也是O(n)。

总结

这个题目,其实是采用动态规划解决最大连续子数组和问题的变种,又多了一层思考。面试者在遇到一个新的题目的时候,不要慌乱,对问题进行仔细分析,进而对其进行分解,分解为自己熟悉的问题。那问题也就解决了。

代码

 1 /**
 2  * 给定一个数组,我们可以找到两个不相交的、并且是连续的子数组A和B,
 3  * A中的数字和为sum(A), B中的元素和为sum(B)。
 4  * 找到这样的A和B,满足sum(A) - sum(B)的绝对值是最大的。
 5  * */
 6 public static int maxSubtractTwoSubArray(int[] nums){
 7     int n = nums.length;
 8
 9     int[] maxLeft = new int[n];
10     int[] minLeft = new int[n];
11     int localMax=nums[0];
12     int localMin=nums[0];
13     maxLeft[0]=nums[0];
14     minLeft[0]=nums[0];
15     for(int i=1; i<n; i++){
16         localMax=Math.max(localMax+nums[i], nums[i]);
17         maxLeft[i]=Math.max(maxLeft[i-1], localMax);
18
19         localMin=Math.min(localMin+nums[i], nums[i]);
20         minLeft[i]=Math.min(minLeft[i-1], localMin);
21     }
22
23     int[] maxRight = new int[n];
24     int[] minRight = new int[n];
25     localMax=nums[n-1];
26     localMin=nums[n-1];
27     for(int i=n-2; i>=0; i--){
28         localMax=Math.max(localMax+nums[i], nums[i]);
29         maxRight[i]=Math.max(maxRight[i+1], localMax);
30
31         localMin=Math.min(localMin+nums[i], nums[i]);
32         minRight[i]=Math.min(minRight[i+1], localMin);
33     }
34
35     int result=Integer.MIN_VALUE;
36     for(int i=1; i<n; i++){
37         result=Math.max(result, maxLeft[i-1]-minRight[i]);
38         result=Math.max(result, maxRight[i]-minLeft[i-1]);
39     }
40
41     return result;
42 }
时间: 2024-10-22 19:50:13

子数组最大和的变化的相关文章

环状连续数组,求子数组最大和

今天看到环状连续数组求子数组最大和的题目,看了几篇博客,但是好像有问题,可以举出反例.于是参考其他人的博客自己又总结下. 首先,求非环状的数组中子数组 最大和问题是一个动态规划的思想. sum[i] = max(sum(i-1) + a[i], a[i]); sum[i]代表以i元素结尾的子数组的最大和,sum[i-1]代表以i-1元素结尾的子数组的最大和,a[i]代表第i个元素的值,由此公式可得,以第i个元素结尾的子数组的最大和可以由它之前的以第i-1个元素结尾的子数组的最大和推导出.如果以i

Maximum Subarray 连续子数组最大和

Find the contiguous subarray within an array (containing at least one number) which has the largest sum. For example, given the array [−2,1,−3,4,−1,2,1,−5,4],the contiguous subarray [4,−1,2,1] has the largest sum = 6. More practice: If you have figur

连续子数组最大和(转)

求一个数组的相加和最大的连续子数组 思路: 一直累加,只要大于0,就说明当前的“和”可以继续增大, 如果小于0了,说明“之前的最大和”已经不可能继续增大了,就从新开始, result=max{result+arr[i],arr[i]};显然,若result>0,则可以继续相加,否则,就重新开始. #include<stdio.h> #define INF 65535 int Maxsum(int * arr, int size) { int maxSum = -INF; int sum

一维数组子数组最大和

题目:返回一个一维整数数组中最大子数组的和. 要求: 输入一个一维整形数组,数组里有正数也有负数. 一维数组首尾相接,象个一条首尾相接带子一样. 数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和. 求所有子数组的和的最大值. 设计思想: 生成数组,生成子数组,求和,求最大值. 代码: package bao; import java.util.*; public class Msum { public static void main(String args[]) { Scanne

最大和子数组/最大和子序列

最大和子数组是数组中和最大的子数组,又名最大和子序列.子数组是数组中连续的n个元素,比如a2,a3,a4就是一个长度为3的子数组.顾名思义求最大和子数组就是要求取和最大的子数组. n个元素的数组包含n个长度为1的子数组:{a0},{a1},…{an-1}: n个元素的数组包含n-1个长度为2的子数组:{a0,a1},{a1,a2},{an-2,an-1}: ……………………………………………………………………………………………… n个元素的数组包含1个长度为n的子数组:{a0,a1,…,an-1

30、剑指offer--连续子数组最大和

题目描述 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学.今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决.但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止).你会不会被他忽悠住?(子向量的长度至少是1) 解题思路:本题时动态规划问题 dp[i]表示到以a[i]结尾的最长连续子数组的最大和 d

结对开发首尾相接数组求子数组最大和

组员:燕亚峰  20122914           王童博   20122823 一.题目及要求: 返回一个整数数组中最大子数组的和 如果数组A[0]...A[j-1]首尾相邻,允许A[i-1]...A[n-1],A[0]...A[j-1]之和最大:同时返回最大子数组的位置. 二.设计思路: 对于这题本想延用一维数组的方法,不过由于数组进行了整合,始末位置无法判断.所以此种方法断然没有实现. 可以形成一个固定长度的窗口,依次相加比较. 首先将为两种情况,一种是跨越a[n-1],a[0]的.一种

首尾相接数组求子数组最大和

一.题目及要求: 返回一个整数数组中最大子数组的和 如果数组A[0]...A[j-1]首尾相邻,允许A[i-1]...A[n-1],A[0]...A[j-1]之和最大:同时返回最大子数组的位置. 二.设计思路: 对于这题本想延用一维数组的方法,不过由于数组进行了整合,始末位置无法判断.所以此种方法断然没有实现. 小伙伴曾说延用课上学生提供的方法,形成一个固定长度的窗口,依次相加比较.这不失为一个好方法.只可惜时间复杂度不是n. 于是上网查了点资料.思想有点引用网上的了.首先将为两种情况,一种是跨

连续子数组最大和

题目来源:<剑指offer>面试题31.<编程之美>2.14 题目:输入一个整形数组,数组里有正数也有负数.数组中一个或连续多个整数组成一个子数组.求所有子数组的和的最大值 解法一:假设id代表自序列的一个起点,j代表终点.如果a[i]是负的,那么它不可能代表最优子序列的起点,因为任何包含a[i]的作为起点的子序列都可以通过a[i+1]作起点得到改进.类似地,任何负的子序列不可能是最优子序列的前缀.比如有数组{1,-2,3,10,-4,7,2,-5}.我们试着从头道尾逐个累加数组中