- 描述
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;
}