算法设计--求连续子向量的最大和问题--论想法思路的重要性

向量[31,-41,59,26,-53,58,97,-93,-23,84]

算法一:直接求解,简单粗暴,没有什么想法可言,复杂度是O(N3)

    // 方法一,接近O(n3)
    int maxsofar1=0;
    int count=0;
    for (int i = 0; i < 10; ++i)
    {
        for (int j = 0; j < 10; ++j)
        {
            int sum=0;
            for (int k = i; k < j; ++k)
            {
                sum+=vec[k];
                count++;
            }
            maxsofar1 = max(maxsofar1,sum);
        }
    }
    cout<<maxsofar1<<" "<<count<<endl;

算法二:算法一的改进,其实算法一的第二个循环和第三个循环是可以直接合并,于是得到了第一个O(n2)的算法

    int maxsofar2=0;count=0;
    for (int i = 0; i < 10; ++i)
    {
        int sum=0;
        for (int j = i; j < 10; ++j)
        {
            sum+=vec[j];
            count++;
            maxsofar2=max(maxsofar2,sum);
        }
    }
    cout<<maxsofar2<<" "<<count<<endl;

对求sum的方法改变一下,可以得到另外一个版本

    int tem[10]={0};
    tem[0]=vec[0];
    for(int i=1;i<10;i++) tem[i]=tem[i-1]+vec[i];
    maxsofar2=0;
    count=0;
    for (int i = 0; i < 10; ++i)
    {
        int sum=0;
        for (int j = i; j < 10; ++j)
        {
            sum=tem[j]-tem[i-1];
            count++;
            maxsofar2=max(maxsofar2,sum);
        }
    }
    cout<<maxsofar2<<" "<<count<<endl;

算法三,分治算法,时间复杂度是O(nLog(n))

分治原理:要解决规模为n的问题,可以递归的解决两个规模接近n/2的子问题,然后对他们的答案进行合并可以得到整个问题的答案

在这个例子中,每次把向量分为左右两个大小近似相等的子向量a和b

                               a  b                                               

分别找出a和b中最大的子向量Ma和Mb

                         Ma                                                                      Mb                                        

那么答案要么在Ma中,要么是Mb中,要么是跨越a和b的边界,为了是递归可以顺利的进行,我们这里的Ma和Mb是从ab的中间分别向两边计算的,

所以如果最大向量在跨越ab的边界的话,那么最大值便是Ma+Mb,要么是Ma或者Mb

int max(int a,int b,int c){return (((a>b)?a:b)>c)?((a>b)?a:b):c;}
int a=0;
int maxsum3(int l,int u)
{
    if(l>u) return 0;//空向量的时候
    if(l==u) return max(0,vec[l]);//向量只有一个元素
    int mid=(l+u)/2;
    int lmax,sum;lmax=sum=0;
    for(int i=mid;i>=l;--i)
    {
        sum+=vec[i];
        lmax=max(lmax,sum);
        a++;
    }
    int rmax;rmax=sum=0;
    for(int j=mid+1;j<=u;++j)
    {
        sum+=vec[j];
        a++;
        rmax=max(sum,rmax);
    }
    return max(lmax+rmax,maxsum3(l,mid),maxsum3(mid+1,u));
}
    cout<<maxsum3(0,9)<<" "<<a<<endl;

算法四:扫描算法,O(n)的时间复杂度就ok了,是线性算法

算法思想:前i个元素中,最大子向量要么在前i-1个元素中,要么其结束位置为i。

这么理解呢?可以这么来理解:

向量[31,-41,59,26,-53,58,97,-93,-23,84]

我们用一个maxsofar4来存储最大的值,用max_ending_here来存储前i个元素中最大的值

以我们这个向量为例,当i=1的时候,maxsofar4=max_ending_here=31,但max_ending_here+vec[1]<0,所以x_ending_here在这里结束了,max_ending_here=0,而maxsofar4不变

当i=2的时候,max_ending_here是用来记录每一次的最大值,当max_ending_here<0 的时候终止,重新记录,而maxsofar4记录的是max_ending_here最大的那个(结果)

    int maxsofar4=0,max_ending_here=0;
    for (int i = 0; i < 10; ++i)
    {
        max_ending_here=max(max_ending_here+vec[i],0);
        maxsofar4=max(maxsofar4,max_ending_here);
    }
    cout<<maxsofar4<<endl;

纵观这四个算法,最简答的想法代码也是相对是比较长的,最复杂的想法代码却是最短的、效率最高的和运行速度最快的,有时候就是这么神奇,所以说有时候不要急着写代码,idea才是最重要的

最后总结一下几个重要的算法设计技术:

  1. 保存状态,避免重复计算

  2. 将信息预处理至数据结构中

  3. 分治算法

  4. 扫描算法:如何将x[0...i-1]的解扩展为x[0....i]的解

  5. 累积:跟2有关系

  6. 下界:最好的时间复杂度

时间: 2024-07-29 07:04:23

算法设计--求连续子向量的最大和问题--论想法思路的重要性的相关文章

C++中求数组连续子向量的最大和(牛客剑指offer)

/////////////////////////////////////////////////////////////////// //HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学. //今天测试组开完会后,他又发话了:在古老的一维模式识别中, //常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决. //但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢? //例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开

一维向量中连续子向量的最大和

Q:一个一维向量:arr[n] = {i1,i2,i3,......,in} ,计算其连续子向量中最大和.(即截取连续的一段使得段中各元素和最大,元素有负值:子向量可以为空,即和最小为0) A: 最初的想法是穷举,双层循环将所有连续的元素和算出来 for [i,n){ for[j,n){ caculate sum(arr[j],arr[j]); } } 这种方式虽然有效,但显得很蠢. 这里假设截取的区间是 arr[start, end] ,那么arr[start, end+1]的连续子向量的最大

计算连续子向量的最大和

题目:HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学.今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决.但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止).你会不会被他忽悠住?(子向量的长度至少是1) package test; import org.junit.Test; public cl

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会

public class Solution { public int FindGreatestSumOfSubArray(int[] array) { int sum=0; int res=array[0]; for (int i=0;i<array.length;i++){ sum = sum + array[i]; if(sum>res){ res=sum; } if(sum<0){ sum=0; } } return res; } } 原文地址:https://www.cnblog

求最大和连续子向量问题的算法分析

1 问题描述 这是从<编程珠玑(第 2 版)>的第 8 章"算法设计技术"中看到的一个问题.问题的描述是这样的, "问题的输入是具有 n 个浮点数的向量 x,输出是输入向量的任何连续子向量中的最大和.例如,如果输入向量包含下面 10个元素:(31,-41,59,26,-53,97,-93,-23,84) 那么该程序的输出为x[2...6] 的总和,即 187." 当所有的数都是正数时,问题很容易解决,此时最大的子向量就是输入向量本身.但如果输入向量中含有

最大子数组问题(求连续子数组的最大和)

在<算法导论>第四章分治策略(Divider and Conquer)4.1节提出了最大子数组问题.其转化就是求数组a={1, -2, 3, 10, -4, 7 , 2, -5}中连续子数组的最大和. 对于这个问题,很容想到一种暴力求解的方法:简单地尝试对所有可能的的组合进行求和.对数组为n存在n*(n-1)/2中组合,然后对每个组合进行求和.即三个for循环遍历,求出数组中每一个子数组的和,最终求出这些子数组的最大的一个值.记Sum[i,...,j]为数组a中第i个元素到第j个元素的和(其中

求连续子数组的最大和

一.题目: 这是一道考的烂的不能再烂的题目,但是依然有很多公司乐于将这样的题目作为笔试或面试题,足见其经典. 问题是这样的:一个整数数组中的元素有正有负,在该数组中找出一个连续子数组,要求该连续子数组中各元素的和最大,这个连续子数组便被称作最大连续子数组.比如数组{2,4,-7,5,2,-1,2,-4,3}的最大连续子数组为{5,2,-1,2},最大连续子数组的和为5+2-1+2=8. 二.解法: 解法一:暴力求解法 /* (1) 常规方法,时间复杂度O(n*n) (2) 先从第一个元素开始向后

经典算法问题 - 最大连续子数列和

文章来自:http://conw.net/archives/9/ (不是抄袭,那是我自己的博客,源地址查看代码有高亮) 最大连续子数列和一道很经典的算法问题,给定一个数列,其中可能有正数也可能有负数,我们的任务是找出其中连续的一个子数列(不允许空序列),使它们的和尽可能大.我们一起用多种方式,逐步优化解决这个问题. 为了更清晰的理解问题,首先我们先看一组数据:8-2 6 -1 5 4 -7 2 3第一行的8是说序列的长度是8,然后第二行有8个数字,即待计算的序列.对于这个序列,我们的答案应该是1

分治法求连续子数组的最大和

思路来自算法导论,将数组平分为左右两个子数组,那么最大子数组可能在左边的子数组中,也有可能在右边的子数组中,还有可能跨过中间的元素,只有这三种情况.对于前两种情况,可以使用递归求解,对于第三种情况,可以做到用线性时间复杂度的函数来求解,详见代码. #include <iostream> #include <map> using namespace std; //the thought of this algrithm is devide-and-couque int int_max