最大子列和问题

本文解析了最大子数列问题在{-4,2,-1,5}

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在计算机科学中,最大子数列问题的目标是在数列的一维方向找到一个连续的子数列,使该子数列的和最大。举一个最简单的例子,给出数组{-4,2,-1,5}求解它的最大子列和。常规思路逐一寻找,具体步骤如下:

①寻找长度为1的子列:{-4},{2},{-1},{5}。数列和分别为:-4,2,-1,5

②寻找长度为2的子列:{-4,2},{2,-1},{-1,5}。数列和分别为:-2,1,4

③寻找长度为3的子列:{-4,2,-1},{2,-1,5}。数列和分别为:-3,6

④寻找长度为4的子列:{-4,2,-1,5}。数列和分别为:2

所以所给数组的最大子列和为:6

暴力求解算法就不多说了,下面给出两种复杂度较低的算法,分治法和在线处理法。注:算法具体思路借鉴浙大陈姥姥mooc讲解。

一、分治法

分支法的大致思想就是将大的问题分解成小的子问题,最后将子问题归纳最终得出最终解,时间复杂度为O(nlogn)。上述例子中,所给出的数组含有4个元素,按照分治思想,将{-4,2,-1,5}分解为{-4,2}和{-1,5}两个子数组,继续分解为{-4},{2},{-1},{5}四个数组分别求解。分解部分完成以后就需要按照要求最分解后的子数组进行合并。

{-42-15}。以中间轴(红色逗号)为分界向两边找4个部分的子列和,得到4个最佳子列(下划线)。它们的最大子列和为5。

{-4,2-1,5}。①中1,2部分合并,3,4部分合并,需要注意的是合并后保证以中间轴(红色逗号)为中心能取到最佳子列,最大子列和为4。

{-4,2-1,5}。对②中的部分再次进行合并,得到最大子列和为6。

算法如下:

///返回三个整数中的最大值
int Max3(int A,int B,int C){
    return A>B?A>C?A:C:B>C?B:C;
}
///分治法求List[left]到List[right]的最大子列和
int DivideAndConquer(int List[],int left,int right){
    int MaxLeftSum,MaxRightSum;///存放左右子问题的结
    int MaxLeftBorderSum,MaxRightBorderSum;///存放跨分界的结果
    int LeftBorderSum,RightBorderSum;///用于左右两边累加计算
    ///当子列中只有1个数字时,递归终止
    if(left==right)
         return List[left];
    ///分的过程:找到中分点
    int center=(left+right)/2;
    ///递归求两边子列的最大和
    MaxLeftSum=DivideAndConquer(List,left,center);
    MaxRightSum=DivideAndConquer(List,center+1,right);
    ///求跨越分界的最大子列和
    MaxLeftBorderSum=List[center];
    LeftBorderSum=List[center];
    ///从中间向左扫描
    for(int i=center+1;i>=left;i--){
        LeftBorderSum+=List[i];
        if(LeftBorderSum>MaxLeftBorderSum){
            MaxLeftBorderSum=LeftBorderSum;
        }
    }
    ///从中间向右边扫描
    MaxRightBorderSum=List[center+1];
    RightBorderSum=List[center+1];
    for(int i=center+2;i<=right;i++){
        RightBorderSum+=List[i];
        if(RightBorderSum>MaxRightBorderSum){
            MaxRightBorderSum=RightBorderSum;
        }
    }
    ///返回治的结果
    return Max3(MaxLeftSum,MaxRightSum,MaxLeftBorderSum+MaxRightBorderSum);
}
///函数调用接口
int MaxSubseqSum3(int List[],int N){
    return DivideAndConquer(List,0,N-1);
}

二、在线处理法(O(n))

在线处理法就是数组从左到右逐一添加元素加和,记录最大子列和,时间复杂度为O(n)。同样,以数组{-4,2,-1,5}进行分析。

设置ThisSum用于累加记录子列和,MaxSum记录当前最大子列和。初始化ThisSum=0,MaxSum=0

①ThisSum累加第1个元素,ThisSum=-4,小于零可以观察到,数组中存在大于零的元素,在这里ThisSum<0,ThisSum中加了负数会使子列和减小。显然不可能为最大子列和,此时无需更新MaxSum,且将ThisSum置0。

②ThisSum累加第2个元素,ThisSum=2,ThisSum>MaxSun=m,更新MaxSum=2。

③ThisSum累加第3个元素,ThisSum=ThisSum+(-1)=1,此时,ThisSum>0且ThisSum<MaxSum,无需操作。

④ThisSum累加第4各元素,ThisSum=ThisSum+5=6,此时,ThisSum>MaxSum,更新MaxSum=6。至此,数组元素全部访问结束,最终得到最大子列和为6。

算法如下:

///在线处理法求解最大子列和
int MaxSubseqSum4(int List[],int N){
    int ThisSum=0,MaxSum=0;///ThisSum用于累加,MaxSum记录最大值
    for(int i=0;i<N;i++){
        ThisSum+=List[i];
        if(ThisSum>MaxSum){
            MaxSum=ThisSum;
        }else if(ThisSum<0){
            ThisSum=0;
        }
    }
    return MaxSum;
}

细心的读者可能已经发现,如果数组元素全部为负数呢,因此,这里就会涉及到一个问题,在线处理法的有效性问题,以上述算法为例,在线处理法可以用来解决数组元素中含有非负值的情况,但当所有元素均为负数时,该算法将会失效,只能借助分治法进行求解。所以,在运用在线处理法时 ,需要首先判断一下数组元素中是否存在非负值,如果不存在,则无法使用在线处理法。

测试代码:

#include<bits/stdc++.h>
using namespace std;

///返回三个整数中的最大值
int Max3(int A,int B,int C){
    return A>B?A>C?A:C:B>C?B:C;
}
///分治法求List[left]到List[right]的最大子列和
int DivideAndConquer(int List[],int left,int right){
    int MaxLeftSum,MaxRightSum;///存放左右子问题的结
    int MaxLeftBorderSum,MaxRightBorderSum;///存放跨分界的结果
    int LeftBorderSum,RightBorderSum;///用于左右两边累加计算
    ///当子列中只有1个数字时,递归终止
    if(left==right){
        if(List[left]>0){
            return List[left];
        }else{
            return 0;
        }
    }
    ///分的过程:找到中分点
    int center=(left+right)/2;
    ///递归求两边子列的最大和
    MaxLeftSum=DivideAndConquer(List,left,center);
    MaxRightSum=DivideAndConquer(List,center+1,right);
    ///求跨越分界的最大子列和
    MaxLeftBorderSum=0;
    LeftBorderSum=0;
    ///从中间向左扫描
    for(int i=center;i>=left;i--){
        LeftBorderSum+=List[i];
        if(LeftBorderSum>MaxLeftBorderSum){
            MaxLeftBorderSum=LeftBorderSum;
        }
    }
    ///从中间向右边扫描
    MaxRightBorderSum=0;
    RightBorderSum=0;
    for(int i=center+1;i<=right;i++){
        RightBorderSum+=List[i];
        if(RightBorderSum>MaxRightBorderSum){
            MaxRightBorderSum=RightBorderSum;
        }
    }
    ///返回治的结果
    return Max3(MaxLeftSum,MaxRightSum,MaxLeftBorderSum+MaxRightBorderSum);
}
///函数调用接口
int MaxSubseqSum3(int List[],int N){
    return DivideAndConquer(List,0,N-1);
}

///在线处理法求解最大子列和
int MaxSubseqSum4(int List[],int N){
    int ThisSum=0,MaxSum=0;///ThisSum用于累加,MaxSum记录最大值
    for(int i=0;i<N;i++){
        ThisSum+=List[i];
        if(ThisSum>MaxSum){
            MaxSum=ThisSum;
        }else if(ThisSum<0){
            ThisSum=0;
        }
    }
    return MaxSum;
}
int main(){
    int s[8]={-1,3,-2,-4,-6,1,6,-1};
    cout<<"分治法:"<<MaxSubseqSum3(s,8)<<endl;
    cout<<"在线处理法:"<<MaxSubseqSum4(s,8);
    return 0;
}

实战:53. 最大子数组和 - 力扣(LeetCode)icon-default.png?t=M5H6https://leetcode.cn/problems/maximum-subarray/submissions/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值