2025年408数据结构算法题

题目描述:给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续 

子数组

(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

leetcode原题:https://leetcode.cn/problems/maximum-product-subarray/description/

首先,很容易想到暴力遍历的方法,也就是将所有的子数组的乘积全部算出来,取最大的即可。

那我们要明确什么是子数组,什么是子串?因为这决定了能否用暴力的方法。

子数组就是必须连续出现在原数组内的数组,而子串是可以不连续的。

题目中给定的数组长度是1e4级别的,如果是子数组,则共有(1+2+3+...+n) ,O(N^2)级别,若是子串,则共有[Cn1 + Cn2 + Cn3 +... + Cnn] = 2 ^N级别,这是一定会TLE的。

在确定可以用暴力的方法之后,我们要知道如何暴力,如果单纯的用一个双层for循环遍历子数组O(N^2)的方法去遍历,那么再加上算积的复杂度O(N),则变成了O(N^3)级别的,也会TLE。

所以我们尝试把算积的复杂度去掉,想到了“前缀积”的方法。

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        int res = nums[0];
        vector<int> pre(n);

        pre[0] = nums[0];
        for(int i = 1; i < n; ++ i)
        {
            pre[i] = pre[i - 1] * nums[i];
        }

        for(int i = 0; i < n; ++ i)
        {
            for(int j = 1; j <= n; ++ j)
            {
                if(i - j >= 0 && pre[i - j])
                    res = max(res, pre[i] / pre[i - j]);
                else
                    res = max(res, pre[i]);
            }
        }

        return res;
    }
};

但是,经过提交发现一个问题,那就是如果原数组nums中出现了一个0,我们的前缀积思想就会失效。

这里会发现,只要突然出现一个0,前缀积数组pre之后所有的项都成了0,这不是简单的加一个res = max{nums[i]}就可以解决的,这个判断仅在这个例子中有效,如果是{1,2,3,0,5,6}呢,这个判断就没有用了。

而面对这种情况,我们应该在遇到0的时候对0左右部分进行分开处理;

对0左边的部分,将其看成一个完成的数组,进行上面的两个for循环筛选出最大子数组的步骤。

对0右边的部分,应该将其重新定为一个新的数组,进行前缀积的操作。

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        int res = nums[0];
        int m = 0;//m表示该段子数组首个非零元素的下标
        vector<int> pre(n);

        pre[0] = nums[0];
        for(int i = 0; i < n; ++ i)
        {
            if(!nums[i])
            {
                res = max(res, nums[i]);
                for(int j = m; j < i; ++ j)
                {
                    for(int k = 1; k <= i - m; ++ k)
                    {
                        if(j - k >= 0 && pre[j - k])
                        {
                            res = max(res, pre[j] / pre[j - k]);
                        }
                        else
                            res = max(res, pre[j]);
                    }
                }

                m = i + 1;
                continue;
            }
            else
            {
                pre[i] = nums[i];
                if(i > 0 && pre[i - 1])
                    pre[i] *= pre[i - 1];
            }
        }

        //对最后一段子数组进行处理
        for(int i = m; i < n; ++ i)
        {
            for(int j = 1; j <= n - m; ++ j)
            {
                if(i - j >= 0 && pre[i - j])
                    res = max(res, pre[i] / pre[i - j]);
                else
                    res = max(res, pre[i]);
            }
        }
        return res;
    }
};

但是发现这样做超时了,所以进行常数优化处理,把循环厘米不必要的去掉。

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        int res = nums[0];
        int m = 0;//m表示该段子数组首个非零元素的下标
        vector<int> pre(n);

        pre[0] = nums[0];
        for(int i = 0; i < n; ++ i)
        {
            if(!nums[i])
            {
                res = max(res, nums[i]);
                for(int j = m; j < i; ++ j)
                {
                    for(int k = 1; k <= 1 + j - m; ++ k)
                    {
                        if(j - k >= 0 && pre[j - k])
                        {
                            res = max(res, pre[j] / pre[j - k]);
                        }
                        else
                            res = max(res, pre[j]);
                    }
                }

                m = i + 1;
                continue;
            }
            else
            {
                pre[i] = nums[i];
                if(i > 0 && pre[i - 1])
                    pre[i] *= pre[i - 1];
            }
        }

        //对最后一段子数组进行处理
        for(int i = m; i < n; ++ i)
        {
            for(int j = 1; j <= 1 + i - m; ++ j)
            {
                if(i - j >= 0 && pre[i - j])
                    res = max(res, pre[i] / pre[i - j]);
                else
                    res = max(res, pre[i]);
            }
        }
        return res;
    }
};

但还是卡了最后一个点,说明这题压根不想让你用暴力。

所以,换贪心的思路。

之所以想到用贪心是因为,这个最大的子数组乘积只能由三条路来,1.直接就是本身nums[i],2.需要前面的乘积最大值乘以nums[i],让它变得更大,3.需要用前面乘积的最小值乘以nums[i],负负得正得到最大的乘积。

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        int res = nums[0];
        int premax = nums[0];
        int premin = nums[0];

        for(int i = 1; i < n; ++ i)
        {
            int curmax = max(nums[i], max(premax * nums[i], premin * nums[i]));
            int curmin = min(nums[i], min(premin * nums[i], premax * nums[i]));

            res = max(res, curmax);
            premax = curmax;
            premin = curmin;
        }
        return res;
    }
};

这样用ON的规模就可以解决这道题了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值