C语言强化(三)发现潜在规律——求子数组的最大和

上一篇解答了在栈里面求最小值元素的问题,这一篇,来聊聊怎么找到数组中子数组的最大和。

通过这道题,你可以掌握

  • 如何根据用户输入创建数组
  • 如何在一连串数字中找到和最大的某一段连续数字子串
  • 如何发现问题的潜在规律并利用这个规律设计算法,解决问题

思路

  • 连续数相加要最大,说明左右两边的数肯定不是负数,否则不可能最大
  • 连续数序列中允许存在负数,前提是负数前面的一段正数相加要大于这个负数,否则两者抵消后,和会变小

算法

遍历数组

遇到正数,不断累加,遇到的第一个正数要记录下标

遇到负数,记录下标,把此下标减1和之前记录的正数的下标之间的数组作为一个可能最大数组,

与之前的可能最大数组比较,若变大,则取代!

判断累加的值与负数大小关系

如果累加值大于负数,则继续累加

如果累加值小于等于负数,舍弃此负数,向前移动,累加值清零

源代码

<span style="font-size:14px;">#include <stdio.h>
#include<stdlib.h>
#include <iostream>
#include<vector>
#include<sstream>

using namespace std;

/**
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为 O(n)。
例如输入的数组为 1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为 3, 10, -4, 7, 2,
因此输出为该子数组的和 18。

思路
连续数相加要最大,说明左右两边的数肯定不是负数
连续数序列中允许存在负数,前提是负数前面的一段正数相加要大于这个负数

算法:
遍历数组

遇到正数,不断累加,遇到的第一个正数要记录下标
遇到负数,记录下标,
把此下标减1和之前记录的正数的下标之间的数组作为一个可能最大数组,
与之前的可能最大数组比较,若变大,则取代!
判断累加的值与负数大小关系
	如果累加值大于负数,则继续累加
	如果累加值小于等于负数,舍弃此负数,向前移动,累加值清零
*/
void main()
{
	//根据用户输入创建数组
	vector <int> oriArray;
	int n=0,count=0;
	string str;
	bool endFlag=true;
	while(endFlag){
		cout<<"请输入第"<<count<<"个数组元素,输入e结束输入"<<endl;
		cin>>str;
		if(str=="e"){
			endFlag=false;
		}else{
			stringstream(str)>> n;
			oriArray.push_back(n);
			count++;
		}
	}
	cout<<"所输入的数组为"<<endl;
	for(int i =0;i<oriArray.size();i++){
		cout<<oriArray[i]<<"  ";
	}

	//求最大子数组
	/**
	add:累加值
	ori:累加值的起始角标
	max:最大和
	maxOri:最大和子数组起始角标
	maxEnd:最大和子数组结尾角标
	*/
	int add=0,ori=0,max=0,maxOri=0,maxEnd=0;
	bool firstPos=true;
	//遍历
	for(int i =0;i<oriArray.size();i++){
		//遇到正数
		if(oriArray[i]>=0){
			add+=oriArray[i];//不断累加
			if(firstPos){//遇到的第一个正数要记录下标
				ori=i;
				firstPos=false;
			}
		}else{
			//遇到负数
			//之与前的可能最大和比较,若变大,则取代!
			if(add>max){
				max=add;
				maxOri=ori;
				maxEnd=i-1;
			}
			/**
			判断累加的值与负数大小关系
				如果累加值大于负数,则继续累加
				如果累加值小于等于负数,舍弃此负数,向前移动,累加值清零
			*/
			if(oriArray[i]+add>0){
				add+=oriArray[i];
			}else{
				add=0;
				if(i+1<oriArray.size())
					ori=i+1;
			}
		}
	}
	//跳出循环后再判断一次
	if(add>max){
		max=add;
		maxOri=ori;
		maxEnd=oriArray.size()-1;
	}
	cout<<endl;
	cout<<maxOri<<"  "<<maxEnd<<endl;
	cout<<"最大子数组的最大值为"<<max<<endl;
	cout<<"最大子数组为"<<endl;
	for(int i=maxOri;i>=maxOri&&i<=maxEnd;i++){
		cout<<oriArray[i]<<"  ";
	}
	system("pause");
}</span>

运行图

此题的关键在于发现最大和子数组的两端不能是负数这个规律。

做完之后在网上找了找类似的题目答案,发现有大神给出了更牛的解法,在此共享一下

思路2:

当前面的几个数,加起来后,b<0后,

把 b 重新赋值,置为下一个元素,b=a[i]。

当 b>sum,则更新 sum=b;

若 b<sum,则 sum 保持原值,不更新

源代码2:

<span style="font-size:14px;">#include <stdio.h>
#include<stdlib.h>
#include <iostream>
#include<sstream>

using namespace std;

/**
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为 O(n)。
例如输入的数组为 1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为 3, 10, -4, 7, 2,
因此输出为该子数组的和 18。

思路
当前面的几个数,加起来后,b<0后,
把 b 重新赋值,置为下一个元素,b=a[i]。
当 b>sum,则更新 sum=b;
若 b<sum,则 sum 保持原值,不更新
*/
int maxSum(int* a, int n)
{
	int sum=0;
	int b=0;
	for(int i=0; i<n; i++)
	{
		if(b<=0) //前面的几个数,加起来后,b<0
			b=a[i];//b 重新赋值
		else
			b+=a[i];//前面的几个数,加起来后,b>=0,继续累加
		if(sum<b)
			sum=b;// b>sum,则更新 sum=b
	}
	return sum;
}

void main()
{
	int a[10]={1,-8,6,3,-1,5,7,-2,0,1};
	cout<<"子数组的最大和为"<<maxSum(a,10)<<endl;
	system("pause");
}</span>

解法二之所以比解法一简练,在于他不仅仅意识到两端的数不能为负数,而且只要那一串的子数组相加小于0,就不可能是最大和子数组的一部分。

每个问题都有其发生的规律,设计算法的过程就是发现规律并加以利用的过程。

就好比打羽毛球,如果我发现只要我一回高远球,对手就放短球,那么我下次回完高远就直接冲到网前准备扑杀。

时间: 2024-12-27 07:35:30

C语言强化(三)发现潜在规律——求子数组的最大和的相关文章

【编程题目】求子数组的最大和 ☆

3.求子数组的最大和(数组)题目:输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.要求时间复杂度为 O(n).例如输入的数组为 1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为 3, 10, -4, 7, 2,因此输出为该子数组的和 18. 算法里学过,动态规划.具体思路想不起来了,看了看书.要动态算1-i个元素中必须包括第i个元素的最大子段和C[i],A是原始序列 C[i + 1] = A[

微软算法100题03 求子数组的最大和

3.求子数组的最大和题目:输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.要求时间复杂度为O(n).例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,因此输出为该子数组的和18 思路:使用辅助变量max 记录子数组的最大和,从数组头开始遍历,如果数组元素与之前的总和的sum是负数,则重置sum结果为零,否则将得到的sum值与max比较 如果小于ma

[华为机试练习题]56.求子数组的最大和

题目 描述: 输入一个整形数组.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值. 接口 Int GetSubArraySum(Int* pIntArray,Int nCount): 规格 要求时间复杂度为O(n) 举例 例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2, 因此输出为该子数组的和18 练习阶段: 初级 代码 /*-------------------------------

求子数组的最大和要求O(n)

//求子数组的最大和 //输入一个整形数组.有整数也有负数,数组中连续一个或多个子数组,每一个子数组都有一个和,求全部子数组的和的最大值,要求时间复杂度O(n) #include<iostream> int GetMax( int * arr) { int max = arr[0]; for (int i = 1; i < 10; i++) { if (max < arr[i]) { max = arr[i]; } } return max; } int getMaxSum(int

Java实现:求子数组的最大和(数组)

求子数组的最大和(数组) 题目: 输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.要求时间复杂度为 O(n). 例如输入的数组为 1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为 3, 10, -4, 7, 2,因此输出为该子数组的和 18. 解题思路: 我觉得用户关心的的数据有两个,一个是子数组的和,还有一个是子数组具体有哪些元素,所以我们可以封装一个子数组类SubArray,成员有子数据开

算法 - 求子数组的最大和(C++)

//**************************************************************************************************** // // 求子数组的最大和 - C++ - by Chimomo // // 题目:输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和. // 求所有子数组的和的最大值.要求时间复杂度为O(n). // 例如输入的数组为5, -22, 13

求子数组的最大和 【微软面试100题 第三题】

题目要求: 输入一个整型数组,数组里有整数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有个一个和. 求所有子数组的和的最大值.要求时间复杂度为O(n). 例如:输入的数组为1,-2,3,10,-4,7,2,-5,和最大的子数组为3,10,-4,7,2,因此输出为该子数组的和18. 参考资料:剑指offer第31题.编程之美2.14. 题目分析: 依次读入数组值,采用两个临时变量 maxSum:保存当前数组元素之前的子数组的最大值: maxEndingHere:包含当前元素前一

求子数组的最大和

输入一个整形数组.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值. 接口 Int GetSubArraySum(Int* pIntArray,Int nCount): 规格 要求时间复杂度为O(n) 举例 例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2, 因此输出为该子数组的和18 华为OJ上的这道题目与算法导论中最大子序列求和略微不同,在这里没有规定如果数组全为负数的话那么我们就定义

求子数组的最大和及下标

转载请注明出处:http://www.cnblogs.com/wuzetiandaren/ 声明:现大部分文章为寻找问题时在网上相互转载,此博是为自己做个记录记录,方便自己也方便有类似问题的朋友,故原出处已不好查到,如有侵权,请发邮件表明文章和原出处地址,我一定在文章中注明.谢谢. 题目:输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.要求时间复杂度为O(n). 例如输入的数组为1, -2, 3, 10, -4,