zoukankan      html  css  js  c++  java
  • 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,就不可能是最大和子数组的一部分。

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

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

  • 相关阅读:
    ASP.NET Core 中文文档 第四章 MVC(3.2)Razor 语法参考
    ASP.NET Core 中文文档 第四章 MVC(3.1)视图概述
    ASP.NET Core 中文文档 第四章 MVC(2.3)格式化响应数据
    ASP.NET Core 中文文档 第四章 MVC(2.2)模型验证
    ASP.NET Core 中文文档 第四章 MVC(2.1)模型绑定
    ASP.NET Core 中文文档 第四章 MVC(01)ASP.NET Core MVC 概览
    mysql 解除正在死锁的状态
    基于原生JS的jsonp方法的实现
    HTML 如何显示英文单、双引号
    win2008 r2 服务器php+mysql+sqlserver2008运行环境配置(从安装、优化、安全等)
  • 原文地址:https://www.cnblogs.com/javdroider/p/5184299.html
Copyright © 2011-2022 走看看