[leetcode oj]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.

题目解析:题目要求在一个整数数组中找到一个连续的子数组,使得这个子数组中所有元素的成绩最大。

很naive的想法就是两个for循环,依次计算任意两个下标之间的乘积,然后找到最大的。这个算法的时间复杂度为O(n*n),空间复杂度为常数。但是会在数组很长的情况下超时。所以,为了寻找更快的算法,需要对这个问题进行一下分析:

整数包括正整数,负整数和零。一个拥有最大乘积的子数组会满足如下的条件:

(1)子数组起始的前一个元素是0或负数;

(2)子数组结束的后一个元素是0或负数;

(3)两端最多只有一个负数;

因此,要考虑如何得到子问题:显然,0是分界点,子数组中一定不会包含0,除非只有负数和零交替出现。

数组中的0将数组分割成了一些segments。其中,每个segment中都是不为0的整数。这样全局的最大可以等价于这个segments中的最大值中的max。而如果出现所有segments都为负数,那么最大值取0,因为0为边界值。(这就是上面说的特殊情况)

下面就是最复杂的一部分:在代码中给出了如何计算不包含零的序列中最大值的思路。

即,将数组分成三部分:起始到第一个为负的情况,最后一个负值到结束,和中间部分。

当然,这又要分很多种情况考虑,因为并不是每个序列都可以刚刚好被分成三段,有可能只有两段或一段的情况:

(1)只有一个negative element(两段)

(2)没有negative element(一段)

(3)只有两个连着的negative element(两段)

为什么要这么分呢?因为中间那段乘积要么为正,要么为负;如果为正,说明有偶数个负值,直接求全部的乘积;如果为负,需要考虑包含左边还是右边的某一段。两边的两段一定为负。

思路c++代码如下:

class Solution {
public:
	int static maxProduct(int A[], int n) {
		int begin, end, result, max;
		begin = 0;
		vector<int> product;
		/*first consider the zero seperation points
		if there exist zero point at position i, i.e A[i] == 0
			divide array into different sections
			find begin and end of the sections
			The point is how to find exact the begin and end position ?
			condition 1: zeros at beginning
			condition 2: non-zeros at ending
			condition 3: zeros in the middle
			condition 4: no zeros
		*/
		for (int i = 0; i < n; i++){
			if (A[i] == 0 || i == n - 1){
				if (A[i] == 0)
					end = i - 1; //condition 1 and 3
				else
					end = i; //condition 2 and 4
				result = subMaxPro(A, n, begin, end);
				if (result < 0 && A[i] == 0)
					product.push_back(0);
				else
					product.push_back(result);
				begin = i + 1;
			}
		}
		max = product[0];
		for (int i = 0; i < product.size(); i++){
			if (product[i] > max)
				max = product[i];
		}
		return max;
	}
	/*
	subMaxPro()compute the max value of segment A[begin..end] of an array 
	But there are many situations to consider:
	1 begin > end (the beginning is zero)
	2 begin == end (return A[begin])
	3 begin < end (consider different situations)
		core of the algorithms........
		find the first negative and last negative elements
		compute the products of three part below:
			first = A[begin...first negative]
			mid = A[first negative + 1...last negative - 1]
			last = A[last negative - 1...end]
			still many situations to consider....
				no negative elements....(give the initial value -1)
				only one negative element.....(first negative index == last negative index)
				more than one negative elements....(first negative index < last negative index)
					no middle values...first negative index == last negative index - 1
					has middle values....
	*/
	int static subMaxPro(int A[], int n, int begin, int end){
		int first, mid, last;
		int idxFirst = -1, idxLast = -1;
		if (begin > end)
			return 0;
		else if (begin == end)
		{
			return A[begin];
		}
		first = 1;
		for (int i = begin; i <= end; i++){
			first = first * A[i];
			if (A[i] < 0){
				idxFirst = i;
				break;
			}
		}
		last = 1;
		for (int i = end; i >= begin; i--){
			last = last * A[i];
			if (A[i] < 0){
				idxLast = i;
				break;
			}
		}
		mid = 1;
		//there are negative elements
		if (idxFirst != -1 && idxLast != -1){
			for (int i = idxFirst + 1; i < idxLast; i++){
				mid = mid * A[i];
			}
		}
		//there is no negative element
		else{
			for (int i = begin; i <= end; i++)
			{
				mid = mid * A[i];
			}
		}
		int max;
		//only one negative values
		if (idxFirst == idxLast && idxFirst != -1){
			int a, b;
			a = first / A[idxFirst];
			b = last / A[idxLast];
			if (a > b)
				max = a;
			else
				max = b;
		}
		// no negative value
		else if(idxFirst == -1 && idxLast == - 1){
			max = mid;
		}
		//more than one negative values
		else
		{
			if (mid > 0)
				max = mid * first * last; // bugs...
			else{
				int a, b;
				a = mid * first;
				b = mid * last;
				if (a > b)
					max = a;
				else
					max = b;
			}
		}
		return max;
	}
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值