乘积最大子序列

 算法题:乘积最大子序列
写一个函数找出一个序列中乘积最大的连续子序列,其中序列中至少包含一个数)。

格式:

第一行输入一个数组,最后依次输出最大乘积的子序列及其乘积的大小。

样例输入

[ 2,3,-2,4 ]

样例输出

[ 2,3 ]

6


java版本的代码实现:

package cn.cat.algorithm;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MaxProductSubSequence {

	/**
	 * 分析:拆分数组,然后分别计算各数组的乘积,乘积最大的数组即为乘积最大子序列。
	 * 拆分规则:
	 * 	1、先根据0进行拆分。
	 *  2、然后判断负数的元素个数,如果个数为偶数,则无须再拆分(因为偶数个负数相乘必定为正数)。
	 *  3、如果负数个数为奇数,则需要细分为两种情况:
	 *  		情况一:负数只有一个,则直接以负数为中点,把数组拆分为三个。
	 *  		情况二:负数个数大于1,也是将数组分为两个	:
	 *  						第一个数组范围为 数组最前端 至 倒数第二个负数索引
	 *  						第二个数组范围为 第二个负数 索引至 数组最末端。
	 * @Description: 
	 * @author gwj
	 * @Created 2018年4月16日 下午2:05:30 
	 * @param args
	 */
	public static void main(String[] args) {
		int[] data = new int[]{ 2, 3, -2, 4 };
		
		//统计0的个数和负数个数
		int countZero  = 0;
		for (int ele : data) {
			if (ele == 0 ) {
				countZero ++;
			}
		}
		
		//1、按零进行分割
		List<ArrRang> segmentsByZero = new ArrayList<ArrRang>(countZero + 1);
		if (countZero == 0) {
			segmentsByZero.add(newArrRang(0, data.length - 1));
		} else {
			//被拆分的数组元素起始索引
			int startDataIndex = 0;
			for (int i = 0; i < data.length; i++) {
				if (data[i] == 0) {
					//0被单独分割是一组
					segmentsByZero.add(newArrRang(i, i));
					//0前面的所有非0元素被分割为一组。
					if (i > 0) {
						segmentsByZero.add(newArrRang(startDataIndex, i - 1));
					}
					startDataIndex = i + 1;
				}
			}
			//0后面的元素被分割为一组
			if (startDataIndex < data.length) {
				segmentsByZero.add(newArrRang(startDataIndex, data.length - 1));
			}
			
		}
		
		//2、按负数进行分割
		List<ArrRang> segmentsByNegative = new ArrayList<ArrRang>();
		for (ArrRang arrRang : segmentsByZero) {
			int countNegative = 0;
			for (int i = arrRang.startIndex; i <= arrRang.endIndex; i++) {
				if (data[i] < 0) {
					countNegative ++;
				}
			}
			
			if (countNegative == 0 || countNegative % 2 == 0) {
				//没有负数,或负数个数为偶数,则不用再分割
				
				segmentsByNegative.add(arrRang);
				
			} else if (countNegative == 1) {
				//负数个数为1个,以负数中心,将数组拆分为三段。
				
				for (int i = arrRang.startIndex; i <= arrRang.endIndex; i++) {
					if (data[i] < 0) {
						//负数单独为一组
						segmentsByNegative.add(newArrRang(i, i));
						//负数左边为一组
						if (i > arrRang.startIndex) {
							segmentsByNegative.add(newArrRang(arrRang.startIndex, i - 1));
						}
						//负数右边为一组
						if (i < arrRang.endIndex) {
							segmentsByNegative.add(newArrRang(i + 1, arrRang.endIndex));
						}
					}
				}
				
			} else {
				//奇数个负数,且个数大于1,需要分割为左右两段数组。
				
				//第二个负数的索引
				int secondNegativeIndex = -1;
				//倒数第二个负数的索引
				int reverseSecondNegativeIndex = -1;
				int newCountNegative = 0;
				for (int i = arrRang.startIndex; i <= arrRang.endIndex; i++) {
					if (data[i] < 0) {
						newCountNegative ++;
					}
					
					if (newCountNegative == 2 && secondNegativeIndex == -1) {
						secondNegativeIndex = i;
					}
					
					if (newCountNegative + 1 == countNegative) {
						reverseSecondNegativeIndex = i;
					}
				}
				
				segmentsByNegative.add(newArrRang(0, reverseSecondNegativeIndex));
				segmentsByNegative.add(newArrRang(secondNegativeIndex, data.length - 1));
				
			}
			
		}
		
		
		int maxProduct = Integer.MIN_VALUE;
		ArrRang maxArrRang = null;
		for (ArrRang arrRang : segmentsByNegative) {
			int product = 1;
			for (int i = arrRang.startIndex; i <= arrRang.endIndex; i++) {
				product *= data[i];
			}
			if (maxProduct < product) {
				maxProduct = product;
				maxArrRang = arrRang;
			}
			
		}
		
		int[] maxSequence = Arrays.copyOfRange(data, maxArrRang.startIndex, maxArrRang.endIndex + 1);
		System.out.println("原数组为:" + Arrays.toString(data));
		System.out.println("最大乘积的子序列为:" + Arrays.toString(maxSequence) + " , 乘积为:"+ maxProduct);
	}

	
	/**
	 * 创建表示数组范围的对象。
	 */
	static ArrRang newArrRang(int startIndex, int endIndex) {
		return new ArrRang(startIndex, endIndex);
	}

	
	/**
	 * 数组范围类。标识下标的起点和终点。
	 */
	static class ArrRang{
		int startIndex;
		int endIndex;
		public ArrRang(int startIndex, int endIndex) {
			this.startIndex = startIndex;
			this.endIndex = endIndex;
		}
	}
}








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值