LeetCode53/152 最大子序和/乘积最大子序列

原题目
最大子序和:

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-subarray

乘积最大子序列

给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-product-subarray

题目分析
第一题:

动态规划:
如果前面的数相加和大于0,就加上当前位置的值,否则,就让其等于当前位置的值,然后与前面存储最大值进行比较。因为如果和为负数,则说明前面的值对后面的增量毫无用处,一个负数加上当前值肯定小于当前值,可以直接舍弃,保存当前值作为新和值,以此规律往下就可以找到最大和。
动态规划的是首先对数组进行遍历,当前最大连续子序列和为 sum,结果为 Maxsum
如果 sum > 0,则说明 sum 对结果有增益效果,则 sum 保留并加上当前遍历数字
如果 sum <= 0,则说明 sum 对结果无增益效果,需要舍弃,则 sum 直接更新为当前遍历数字
每次比较 sum 和 Maxsum的大小,将最大值置为Maxsum,遍历结束返回结果
时间复杂度:O(n)

分治法:
将大问题划分成许多子问题,往下划分直到不能划分为止。
假设我们有一个函数seach(int left,int right,int *nums),可以得到num[ left , right )(左包右包)中子数组最大值。
如果,left==right,那么search直接返回nums[left]即可。

if(left==right)
return nums[left];

然后对问题进行分解
先找一个 mid , mid = ( left + right ) / 2。

然后,对于我们要找的和最大的子数组有两种情况。

mid 不在我们要找的子数组中

这样的话,子数组的最大值要么是 mid 左半部分数组的子数组产生,要么是右边的产生,最大值的可以利用 search 求出来。

int lmax_sum = search(left, mid, nums);
int rmax_sum= search(mid + 1, right, nums);

mid 在我们要找的子数组中
这样的话,我们可以分别从 mid 左边扩展,和右边扩展,找出两边和最大的时候,然后加起来就可以了。当然如果,左边或者右边最大的都小于 0 ,我们就不加了。

int search_mid(int left,int mid,int right,int *nums)
{
    int lmax_sum=0,rmax_sum=0,sum=0;
    for(int i=mid-1;i>=left;i--)
    {
        sum+=nums[i];
        if(lmax_sum<sum)
            lmax_sum=sum;
    }
    sum=0;
    for(int i=mid+1;i<=right;i++)
    {
        sum+=nums[i];
        if(rmax_sum<sum)
            rmax_sum=sum;
    }
    return lmax_sum+nums[mid]+rmax_sum;
}

最后,我们只需要返回这三个中最大的值就可以了。
综上,递归出口,问题分解就都有了。

第二题:

方法一:动态规划
因为,有负数存在,所以一个正数乘以一个负数就会变成最小,所以每次存储上一次最大值和上一次最小值,然后与当前值进行比较,更新最大值和最小值,再将最大值和返回值进行比较,更新返回值。

方法二:
思路: 求最大值,可以看成求被0拆分的各个子数组的最大值。
当一个数组中没有0存在,则分为两种情况:
1.负数为偶数个,则整个数组的各个值相乘为最大值;
2.负数为奇数个,则从左边开始,乘到最后一个负数停止有一个“最大值”,从右边也有一个“最大值”,比较,得出最大值。

完整代码
第一题:

动态规划:

int maxSubArray(int* nums, int numsSize){
    int sum=0,Maxsum=INT_MIN;
    for(int i=0;i<numsSize;i++)
    {
        if(sum>0)
        {
            sum+=nums[i];
        }
        else
        {
            sum=nums[i];
        }
        Maxsum=sum>Maxsum?sum:Maxsum;
    }
    return Maxsum;
}

分治法:

int search_mid(int left,int mid,int right,int *nums)
{
    int lmax_sum=0,rmax_sum=0,sum=0;
    for(int i=mid-1;i>=left;i--)
    {
        sum+=nums[i];
        if(lmax_sum<sum)
            lmax_sum=sum;
    }
    sum=0;
    for(int i=mid+1;i<=right;i++)
    {
        sum+=nums[i];
        if(rmax_sum<sum)
            rmax_sum=sum;
    }
    return lmax_sum+nums[mid]+rmax_sum;
}

int search(int left,int right,int *nums)
{
    if(left==right)
        return nums[left];
    int mid=(left+right)/2;
    int lmax_sum=search(left,mid,nums);
    int rmax_sum=search(mid+1,right,nums);
    int mmax_sum=search_mid(left,mid,right,nums);
    return fmax(mmax_sum,fmax(lmax_sum,rmax_sum));
}

int maxSubArray(int* nums, int numsSize){
    return search(0,numsSize-1,nums);
}

第二题:
方法一:动态规划:

int maxProduct(int* nums, int numsSize){
    int Max=1,Min=1,res=INT_MIN;
    for(int i=0;i<numsSize;i++)
    {
        int a=nums[i]*Max;
        int b=nums[i]*Min;
        Min=fmin(nums[i],fmin(a,b));
        Max=fmax(nums[i],fmax(a,b));
        res=fmax(Max,res);
    }
    return res;
}

方法二:

int maxProduct(int* nums, int numsSize){
    int res=nums[0];
    int pro1=1,pro2=1;
    int i=0,j=numsSize-1;
    while(i<numsSize&&j>-1)
    {
        pro1=pro1*nums[i++];
        res=fmax(res,pro1);
        pro2=pro2*nums[j--];
        res=fmax(res,pro2);
        if(pro1==0)
            pro1=1;
        if(pro2==0)
            pro2=1;
    }
    return res;
}
总结

掌握动态规划,分治法等基本算法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Baal Austin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值