package com.xhm.sjjg;
/**
* 求最大子序列和的问题
* 问题描述: 给定一串int型数字(可正可负,本例中使用数组实现),
* 寻求其中的一个子序列, 要求其所有项的值相加之和,
* 为这一串数字所存在的子序列中各项的值相加之和为最大的,
* 并返回那个序列各项的值相加之和。
*/
public class SubsqueenceTest
{
/**
* 方法1:一般实现方式
* 该方式清晰明了,分别计算每个可能的子序列之和
* 然后通过比较,始终将每个可能的子序列之和的最大值进行保存返回
* 但该方式效率最低 O(N^3)
*/
public static int getMaxSubSum1(int[] sub)
{
int maxSum = 0;
for (int i = 0; i < sub.length; i++)
{
for (int j = i; j < sub.length; j++)
{
int sum = 0;
for (int n = i; n <= j; n++)
{
sum += sub[n];
}
if (sum > maxSum)
{
maxSum = sum;
}
}
}
return maxSum;
}
/**
* 方法2:针对方法1优化过的实现 O(N^2)
*/
public static int getMaxSubSum2(int[] sub)
{
int maxSum = 0;
for (int i = 0; i < sub.length; i++)
{
int sum = 0;
for (int j = i; j < sub.length; j++)
{
sum += sub[j];
if (sum > maxSum)
{
maxSum = sum;
}
}
}
return maxSum;
}
/**
* 返回三个int型数字的最大值
*/
private static int max(int a, int b, int c)
{
int max = 0;
if (a > b)
{
max = a;
} else
{
max = b;
}
if (c > max)
{
max = c;
}
return max;
}
/**
* 该方法为方法3调用
* 通过递归来获取最大子序列的和
*/
private static int maxSubRecursive(int[] sub, int left, int right)
{
if (left == right)
{
return sub[left];
}
int center = (left + right) / 2;
// 用来记录左部分的最大子序列之和
int maxLeft = maxSubRecursive(sub, left, center);
// 用来记录右部分的最大子序列之和
int maxRight = maxSubRecursive(sub, center + 1, right);
// 用来记录左部分包含最后一个元素的最大子序列之和
int maxLeftBorder = 0;
int leftBorder = 0;
for (int i = center; i >= left; i--)
{
leftBorder += sub[i];
if (leftBorder > maxLeftBorder)
{
maxLeftBorder = leftBorder;
}
}
// 用来记录右部分包含第一个元素的最大子序列之和
int maxRightBorder = 0;
int rightBorder = 0;
for (int i = center + 1; i <= right; i++)
{
rightBorder += sub[i];
if (rightBorder > maxRightBorder)
{
maxRightBorder = rightBorder;
}
}
return max(maxLeft, maxRight, maxLeftBorder + maxRightBorder);
}
/**
* 方法3:采用“分治”策略 O(N*logN)
* 基本思想:
* 将数字序列分为2部分,
* 那最大和的出现将存在3种可能:1、左部分 2、右部分 3、跨越左右两部分,占据序列的中间
* 取他们中间的最大者即为最大子序列的和
*/
public static int getMaxSubSum3(int[] sub)
{
return maxSubRecursive(sub, 0, sub.length - 1);
}
/**
* 方法4:动态规划实现 O(N)
* 原理:主要在子序列的起点定位上
* 在数字序列中任何一个以负数作为起点的子序列,
* 都不可能是最优序列的起点,我们总是可以其后边的项作为起点来优化它
* 同理,任何负的子序列也不可能是最优序列的起点
*/
public static int getMaxSubSum4(int[] sub)
{
int maxSum = 0;
int sum = 0;
for (int i = 0; i < sub.length; i++)
{
sum += sub[i];
if (sum > maxSum)
{
maxSum = sum;
} else if (sum < 0)//关键
{
sum = 0;
}
}
return maxSum;
}
public static void main(String[] args)
{
int[] sub = { 3, 11, -7, 15, -5, -2 ,23,-3,10};
System.out.println("getMaxSubSum1 = "
+ SubsqueenceTest.getMaxSubSum1(sub));
System.out.println("getMaxSubSum2 = "
+ SubsqueenceTest.getMaxSubSum2(sub));
System.out.println("getMaxSubSum3 = "
+ SubsqueenceTest.getMaxSubSum3(sub));
System.out.println("getMaxSubSum4 = "
+ SubsqueenceTest.getMaxSubSum4(sub));
}
}