求和最大的子数组

问题描述:

一个数组中包含正数和负数,其中位置相邻的若干元素称为子数组,求总和最大的子数组,举例{1, -2, 3, 10, -4, 7, 2, -5},最大子数组是{3, 10, -4, 7, 2},要求时间复杂度为O(n)

问题分析:

首先考虑临界情况,如果数组中全是负数,则直接返回数组中的最大负数即可,

现在考虑一般情况,即数组中至少存在一个正数,因为时间复杂度要求为 O(n) ,即要求只能对数组进行一次遍历,我们假定从左往右遍历,

我们先简化考虑,将原数组中所有相邻的正数相加,所有相邻的负数也相加,例如:

{-1, -2 ,5 ,-2, 10,16, -2 , -4, -9, 1, 2},简化后就变成{-3, 5 , -2 , 26, -15, 3}

可以看到,简化后的数组正负数是间隔着排列的,现在分析简化后的数组,如下是以从左往右遍历的第一个和第二个正数为端点的子数组的几种可能情况:

1)   5,  -2, 26  ---负数小于两端的正数

2)   5,  -12, 26 --负数大于左边的正数,小于右边的正数

3)  26,  -12, 5 --负数小于左边的正数,大于右边的正数

4)  12,  -26, 5 --负数大于两端的正数

对于上述1),当遍历到第二个正数时,总和最大的子数组是可能包括这个子数组{5, -2, 26}的,并且总和是 5 + (-2)+ 26 = 29,

对于上述2),当遍历到第二个正数时,总和最大的子数组肯定不可能包括第一个正数了,所以可以放心的把第一个正数抛弃,所以子数组为{26},总和是26,

对于上述3),当遍历到第二个正数时,总和最大的子数组肯定包括第一个正数,第二个正数可能包括,因为咱们不知道第二个正数后面还有没有更大的正数,所以

这里既要保留当前总和最大的子数组{26}、最大值26,又要保留子数组{26, -12, 5}、总和 26 + (-12)+ 5 = 19,

对于上述4),当遍历到第二个正数时,总和最大的子数组要么就是子数组{12},要么就包括第二个正数{5},理由同上,因为有可能第三个正数很大,


从上分析可以得出,至少设置4个临时变量:当前总和最大的子数组int[] maxArr及其总和 int max,有可能总和最大的子数组int[] currMaxArr及其总和 int currMax,

这样遍历到第三个正数时,将currMax作为左边的端点,第三个正数作为右边的端点,作上述同样的判断,并将计算得出的最大值和max比较,如果比max大,就替换max和maxArr的值。后续就是不断的重复此过程,一直到数组的最后一个元素。

代码如下:

public static List<Integer> test(int[] n)
	{
		int max = 0;//子数组和的最大值
		List<Integer> maxArr = new ArrayList<Integer>();//最大值对应的子数组
		int currMax = 0;//当前计算的最大值
		List<Integer> currMaxArr = new ArrayList<Integer>();//当前最大值对应的子数组
		
		maxArr.add(n[0]);
		for(int i=0; i<n.length; i++)
		{
			if(currMax < 0)
			{
				currMax = n[i];
				currMaxArr.removeAll(currMaxArr);
				currMaxArr.add(n[i]);
			}
			else
			{
				currMax += n[i];
				currMaxArr.add(n[i]);
			}
			
			if(max < currMax)
			{
				max = currMax;
				maxArr.removeAll(maxArr);
				maxArr.addAll(currMaxArr);
			}
		}
		return maxArr;
	}


### 最大子数组问题 C语言 实现 最大子数组问题是经典的算法问题之一,目标是从一个整数数组中找到具有最大的连续子数组。以下是基于动态规划方法的一种高效实现方式: #### 动态规划方法 通过遍历整个数组并维护两个变量 `current_max` `global_max` 来解决问题。其中: - `current_max` 表示以当前元素结尾的最大子数组。 - `global_max` 表示全局范围内的最大子数组。 具体代码如下所示: ```c int maxSubArray(int* nums, int numsSize) { int current_max = nums[0]; int global_max = nums[0]; for (int i = 1; i < numsSize; i++) { if (current_max > 0) { current_max += nums[i]; // 如果前面的大于零,则加上当前值 } else { current_max = nums[i]; // 否则丢弃之前的,从当前值重新开始 } if (current_max > global_max) { global_max = current_max; // 更新全局最大值 } } return global_max; } ``` 此方法的时间复杂度为 O(n),空间复杂度为 O(1)[^1]。 --- #### 贪心算法方法 另一种常见的解决思路是贪心算法。核心思想是在每一步决策时选择局部最优解,并逐步构建最终的全局最优解。对于本问题而言,在遍历时不断累加当前子数组,并在必要时重置起点。 下面是采用贪心策略的一个例子: ```c int maxSubArray(int* nums, int numsSize){ int preSum = 0, MaxSum = -10000, curSum = nums[0]; for (int i = 0; i < numsSize; i++) { if (i == 0) { preSum = 0; } else { preSum += nums[i - 1]; } if (preSum < 0) { curSum = nums[i]; // 若前缀小于零,则抛弃之前的部分 preSum = 0; } else { curSum = preSum + nums[i]; // 继续累积当前部分 } if (curSum > MaxSum) { MaxSum = curSum; // 更新最大值 } } return MaxSum; } ``` 这种方法同样具备线性时间复杂度 O(n),并且仅需常量额外存储空间 O(1)[^3]。 --- #### 分治法递归实现 除了以上两种迭代式的解决方案外,还可以利用分治的思想来处理这个问题。基本原理在于把原数组分割成两半分别寻找各自内部的最佳结果以及跨过中心点的情况下的最佳组合。 下面展示了一个典型的分治递归版本程序片段: ```c // 辅助函数:求三者之间的最大值 int maxz(int a, int b, int c) { if (a >= b && a >= c) return a; if (b >= a && b >= c) return b; return c; } // 主体逻辑:递归划分区间直到单个元素为止再回溯合并计算 int maxarry(int* nums, int l, int r) { if (l == r) return nums[l]; int mid = (l + r) / 2; int maxl = maxarry(nums, l, mid); int maxr = maxarry(nums, mid + 1, r); int i = mid - 1, j = mid, addl = 0, addr = 0, max1 = 0, max2 = nums[mid]; while (i >= l) { addl += nums[i--]; if (addl > max1) max1 = addl; } while (j <= r) { addr += nums[j++]; if (addr > max2) max2 = addr; } int maxm = max1 + max2; return maxz(maxl, maxr, maxm); } // 接口封装供外部调用 int maxSubArray(int* nums, int numsSize) { return maxarry(nums, 0, numsSize - 1); } ``` 尽管这种方案理论上也达到了 O(n log n) 的效率级别[^2],但由于涉及较多层次嵌套操作故实际运行性能可能稍逊于前述两者简单循环形式的设计。 --- ### 总结 综上所述,针对不同场景可以选择适合自己的算法模型去应对最大子数组求和挑战。无论是追求简洁高效的动态规划还是灵活通用性强但开销相对较高的分而治之技巧都能很好地完成任务需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值