LeetCode 152. Maximum Product Subarray

  • 描述

Find the contiguous subarray within an array (containing at least one number) which has the largest product.

For example, given the array [2,3,-2,4],
the contiguous subarray [2,3] has the largest product = 6.

  • 思路
    我们知道,数有几种,正数、负数、零,这么多数相互组成可以考虑以下几种情况
    1、全部由正数组成。
    2、全部由非负数组成。
    3、由正负数组成或是全由负数组成。
    我们来分析这几种情况
    1、直接全部相乘,得到结果。
    2、假如有【1,2,0,3,4】这样一个数组,那么最大的数一定是在0的左右两边,即【1,2】或者【3,4】,即可以将非负数的组合拆分成正数组合。
    3、由正负数组成或是全由负数组成。这就分为两种情况,如果负数的个数为偶数个,那么直接相乘。如果为奇数个,则是math.Max(第一个负数以后的所有数相乘,最后一个负数的前面所有数字相乘)。为什么是这样呢?举个例子,有数组【3,-1,2,-1,4,5,6,-2,2】,为了尽可能的保持更多的数字相乘,那么就只能选【-1,2,-1,4,5,6】或是【2,-1,4,5,6,-2,2】这样的组合,这两种组合保证了尽可能多的数字相乘,且乘积为正数。
    综合上面的分析,我们容易想到分两步,第一步, 将集合中所有的0去掉,拆分成没有0的组合;第二步,求出所有集合的最大值。在第二步中,在考虑负数为奇数个或为偶数个的情况。
  • 代码(c#)
  public int MaxProduct(int[] nums)
        {
            List<List<int>> list = new List<List<int>>();//保存拆分0后的集合
            List<int> numsFlag = new List<int>();//记录一个集合中负数的个数
            List<int> item = new List<int>();
            int negativeCount = 0;
            int max = int.MinValue;
            for (int i = 0; i < nums.Length; i++)//第一步,将集合进行拆分
            {
                if (nums[i] == 0)
                {
                    max = 0;//最大值可能为0,所以这里要赋值为零
                    if (item.Count > 0)
                    {
                        list.Add(item);//拆分出一个集合
                        numsFlag.Add(negativeCount);//记录该集合的负数个数
                        negativeCount = 0;
                        item = new List<int>();
                    }
                }
                else if (nums[i] != 0)
                {
                    item.Add(nums[i]);
                    if (nums[i] < 0) negativeCount++;//该值为负数,记录出现的次数
                }
            }
            if (item.Count > 0)
            {
                list.Add(item);//将最后一个集合添加进去
                numsFlag.Add(negativeCount);
            }
            for (var i = 0; i < list.Count; i++)//第二步,求出所有集合的最大值
            {
                max = Math.Max(MaxProduct(list[i], numsFlag[i]), max);
            }
            return max;
        }
        public int MaxProduct(List<int> list,int negativeCount)
        {
            if (list.Count == 1)
            {
                return list[0];//只有一个的情况下,最大值只能是它自己
            }
            int max = int.MinValue;
            if (negativeCount % 2 == 0)//负数为偶数个,最大值即为所有值相乘
            {
                max = 1;
                list.ForEach(x => { max *= x; });
                return max;
            }
            //负数个数为奇数个
            int firstIndex = list.FindIndex(x => x < 0);//找到第一个负数的索引
            int lastIndex = list.FindLastIndex(x => x < 0);//找到最后一个负数的索引
            if (firstIndex != list.Count - 1)
            {
                max = 1;
                for (int i = firstIndex + 1; i < list.Count; i++)
                {
                    max *= list[i];//求出第一个负数之后的所有的值的乘积
                }
            }
            int max2 = int.MinValue;
            if (lastIndex != 0)
            {
                max2 = 1;
                for (int i = 0; i < lastIndex; i++)
                {
                    max2 *= list[i];//求出最后一个负数前面所有的值的乘积
                }
            }
            return Math.Max(max, max2);//最大值即是这两者之一
        }
  • 另外的参考思路
    上面的代码可能思路比较清晰,但是代码实现却比较多,看了其他博客的,发现这题可以使用动态规划思路去做,由于有了负值,因此乘以下一个数时可能是之前已求出的最大值相乘,也可能是该数与最小的值相乘,每次都保存这两种情况,最大或是最小,然后定义一个全部变量,记录已出现的最大值。下面给出代码
  • 代码(c#)
  public int MaxProduct(int[] nums)
        {
            if (nums.Length == 0) return 0;
            int min = nums[0];
            int max = nums[0];
            int globalMax = nums[0];
            for (int i = 1; i < nums.Length; i++)
            {
                int a = min * nums[i];
                int b = max * nums[i];
                max = Math.Max(nums[i],Math.Max(a,b));//记录最大的值
                min=Math.Min(nums[i], Math.Min(a, b));//记录最小的值
                globalMax = Math.Max(max, globalMax);//记录全局最大值
            }
            return globalMax;
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值