算法.动态规划 最大子段和问题(穷举 JAVA实现)

前言

子序列之和最大,即最大子段和问题(Maximum Interval Sum)(有时也称LIS)

子序列:[A1,A2,…,An] 的子序列: [Ai,Aj] ,其中i<=j, 1 <= i,j <= n

求和:[A1,A2,…,An] 求和 = A1 + A2 + … + An

举例

数组:5 4 -1 7 8
方案1:遍历,求sum值,然后比大小
	5					sum = 5
	5 4					sum = 9
	5 4 -1				sum = 8
	5 4 -1 7		    sum = 15
	5 4 -1 7 8			sum = 23

	  4					sum = 4
	  4 -1				sum = 3
	  4 -1 7			sum = 10
	  4 -1 7 8			sum = 18
	   
		-1				sum = -1
		-1 7			sum = 6
		-1 7 8			sum = 14
		   
		   7			sum = 7
		   7 8			sum = 15
		   
			 8			sum = 8

改进方案1:
	1. 每一次遍历都可以使用上次的计算值 + 下一个元素
	2. i++ 就清零,需要重启计算

方案1:穷举

1. 第一层和第二层 是 进行穷举所有子序列

2. 第三次是求 子序列的累加和

	    public int maxSubArray2(int[] nums) {
	    	if(nums == null || nums.length == 0){
	    		return 0;
	    	}
	    	int len = nums.length;
	    	if(len == 1){
	    		return nums[0];
	    	}
	    	// 所有的和最大值,默认去第一个总不会错
	    	int allSumMax = Integer.MIN_VALUE;
	    	// 行最大值
	    	int rowSumMax = 0;
	    	int rowSum = 0;
	    	/**
	    	 * 既然是子序列,那么[-i----------j--] i和j就是在 [0,len]之间,i < j
	    	 * i 从0开始,len-2结束。 len-1 即最后一个元素流给j
	    	 * j 从i开始,len-1结束
	    	 * 从编码看:如果
	    	 */
	    	for (int i = 0; i < len ; i++) {
	    		rowSum = nums[i];
	    		rowSumMax = nums[i];
	    		for (int j = i + 1; j < len; j++) {
	    			// 每次累计
	    			rowSum += nums[j];
	    			// 只有当前数值>0 才可能比原来积累的最大值大,当然也可以比原来的小,是中间添加了交大的负值
	    			if(nums[j] > 0 && rowSum > rowSumMax){
	    				rowSumMax = rowSum;
	    			}
	    		}
	    		// 判断[i]子序列集合求和最大值 跟 最大值 之间的关系
	    		if(rowSumMax > allSumMax){
	    			allSumMax = rowSumMax;
	    		}
	    	}
	    	return allSumMax;
	    }

方案2:对穷举中 求和的改进

 public int maxSubArray1(int[] nums) {
	    	if(nums == null || nums.length == 0){
	    		return 0;
	    	}
	    	int len = nums.length;
	    	if(len == 1){
	    		return nums[0];
	    	}
	    	int sum = Integer.MIN_VALUE;
	    	int tempSum = 0;
	    	/**
	    	 * 既然是子序列,那么[-i----------j--] i和j就是在 [0,len]之间,i < j
	    	 * i 从0开始,len-2结束。 len-1 即最后一个元素流给j
	    	 * j 从i开始,len-1结束
	    	 * 从编码看:如果
	    	 */
	    	for (int i = 0; i < len ; i++) {
	    		for (int j = i; j < len; j++) {
	    			// 求子序列之和 [i,j]
	    			for (int k = i; k <= j; k++) {
	    				tempSum += nums[k];
	    			}
	    			// 如果当前序列,比我原来的大,就记录值
	    			if(tempSum > sum){
	    				sum = tempSum;
	    			}
	    			tempSum = 0;
	    		}
	    	}
	    	
	    	return sum;
	    }

方案3:对其实最大值的改进

思路: 想从这之间找到规律,不就可以遍历一次了。 

问题:1. 编码很麻烦,需要处理特例很多。2. 如果最大值并没有参与运算,下列红框的max之间就没有关系了,也就从根子上没法做。

补充方案:存储 不包含 第一个元素,如果5作为的单个序列的,其他子序列之和最大值,因为后边的最大值跟这个一定有关系。

继续改进:
[i]子序列集合:从上面可以看出i每遍历一个值,j就从i到length-1,形成了一个子序列集合。咱们就陈i子序列集合
[i]子序列集合求和最大值:i子序列每个序列的求和后 比较出来的最大值
1. 假如当前num[i] > 0 ; [i]子序列集合求和最大值 = [i+1]子序列集合求和最大值 + num[i] 
2. 类似:num[i] = 0; [i]子序列集合求和最大值  = [i+1]子序列集合求和最大值
3. 同理:num[i] < 0; [i]子序列集合求和最大值 + num[i]  = [i+1]子序列集合求和最大值 
合并序列集合求和最大值:[i]子序列集合求和最大值  he [i+1]子序列集合求和最大值   合并为 [i,i+1]子序列集合求和最大值
1. 假如当前num[i] > 0 ; [i]子序列集合求和最大值 就是 [i,i+1]子序列集合求和最大值
2. 类似:num[i] = 0; [i]或者[i]子序列集合求和最大值 就是 [i,i+1]子序列集合求和最大值
3. 同理:num[i] < 0; [i+1]子序列集合求和最大值 就是 [i,i+1]子序列集合求和最大值
特例:
1. 全是负数
2. 收尾有包含0的元素,如:001100
3. 元素为0和负数
4. 如果最大值就是第一项,说明第一项就没有参与运算,[i] 和 [i+1]也就没关系了,也就不适用了。
    - 记录不包含 第一项的最大值,这条路走下去就沟里边了

复制一个不怎么完善的代码,可以放在力扣中去执行,把特例第四项补充上,个人不建议做,一个算法补丁这么多,只能说刚开始的建模就有问题。

class Solution {
	    public int maxSubArray(int[] nums) {
	    	if(nums == null || nums.length == 0){
				return 0;
			}
			int len = nums.length;
			if(len == 1){
				return nums[0];
			}
			// 非0开始数组区间
			int startIndex = 0;
			for (int j = 0; j < len; j++) {
				if(nums[j] != 0){
					startIndex = j;
					break;
				}
			}
			int endIndex = 0;
			for (int j = len - 1; j >= 0; j--) {
				if(nums[j] != 0){
					endIndex = j;
					break;
				}
			}
			// 全是0场景
			if(startIndex == len || endIndex == -1){
				return 0;
			}
			
			// 所有的和最大值,默认去第一个总不会错
			int allSumMax = Integer.MIN_VALUE;
			// 计算 [0]子序列集合求和最大值
			int rowSumMax = nums[startIndex];
			int rowSum = nums[startIndex];
			// 所有都是负值
			boolean allNegativeData = nums[startIndex] <= 0;
			int allNegativeMax = nums[startIndex] ;
			for (int j = startIndex + 1; j <= endIndex; j++) {
				// 每次累计
				rowSum += nums[j];
				// 只有当前数值>0 才可能比原来积累的最大值大,当然也可以比原来的小,是中间添加了交大的负值
				if(nums[j] >= 0 && rowSum > rowSumMax){
					rowSumMax = rowSum;
				}
				// 如果出现正数,就不在继续
				if(nums[j] > 0 && allNegativeData){
					allNegativeData = false;
				}
				// 求最大值
				if(nums[j] > allNegativeMax){
					allNegativeMax = nums[j]; 
				}
			}
			// 解决全负数情况
			if(allNegativeData){
				// 前面还有0
				if(startIndex > 0 || endIndex < len - 1){
					return 0;
				}
				return allNegativeMax;
			}
			allSumMax = rowSumMax;
			
			// 遍历处理 [i+1]子序列集合求和最大值
			for (int i = startIndex + 1; i <= endIndex ; i++) {
				rowSumMax -= nums[i - 1];
				
				if(rowSumMax > allSumMax){
					allSumMax = rowSumMax;
				}
			}
			return allSumMax;
	    }
	}

后面继续补充 最大子数组(序列)和的 其他实现方法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值