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,就不可能是最大和子数组的一部分。

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

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

  • 相关阅读:
    whatweb tree
    testUrl
    ParseUrl
    whatweb wordpress.rb
    LeetCode: Binary Tree Level Order Traversal 解题报告
    LeetCode: Minimum Path Sum 解题报告
    Lintcode: Sort Colors II 解题报告
    LeetCode: Validate Binary Search Tree 解题报告
    LeetCode: Longest Common Prefix 解题报告
    LeetCode: Maximum Subarray 解题报告
  • 原文地址:https://www.cnblogs.com/javdroider/p/5184299.html
Copyright © 2011-2022 走看看