声明:题目来自: http://blog.csdn.net/v_JULY_v/archive/2010/11/17/6015165.aspx JULY整理了100道微软等公司的面试题目,我想先不看答案:http://blog.csdn.net/v_JULY_v/archive/2011/01/10/6126406.aspx 自己先做一遍。
题目:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,
因此输出为该子数组的和18。
思路:
一般情况下,我都是先举一些具体的例子,在纸上比划比划,看看在比划的过程中有没有什么规律可循。首先我想到的还是dynamic programming 的思路,但是我总结不出sub optimal structure来,你说 一个数组A[0..K] 的最大和子数组和 A[0..K-1]有什么关系呢?所以接下来我们看几个例子:
情况1: [ -1 -2 -3 4 5 6 -4 -5 ] 这个例子中,显然最大和子数组是 4 5 6, 那我是不是可以下结论说,首先把给定的数组的左边和右边的负数都去掉?我一开始真这么想来着,后来我同事说:“如果整个给定的数组中全是负数呢?”,是呀!我没有考虑到这个:
情况2: [ -5 -4 -1 -2 -3 -8 ] 如果全是负数,那最大子数组是 -1, 如果我按照上面的思路,在预处理的时候分别从最左边和最右边把负数都先去掉,那就没有最大和子数组存在了! 教训1:在纸上举例子比划的时候,一定不要忘记边界情况,在这里是 全是正数,或者全是负数!
再看,在全是负数的时候,其实每次我们拿到一个新的数A[k], 我都认为这有可能是一个最大子数组,初始化的时候 maxSum= A[0], 然后迭代整个数组,if(A[k] > maxSum) { maxSum = A[k]; maxBegin = k, maxEnd=k+1} 。 这里,maxBegin 和 maxEnd用来记录 随后的最大子数组,最大子数组的元素为 A[i], maxBegin <= i < maxEnd; maxSum是指到目前位置最大子数组的和。
情况3: [ -5 -4 -1 1 2 3 ] 当我们读到 1 的时候, 之前记录的最大的元素位置maxBegin=2,maxEnd=3 最大值是A[maxBegin] = -1, 当前元素位置currentIndex=3, 当前值A[currentIndex] = 1, 于是我们应该更新 maxBegin=currentIndex=3, maxEnd=maxBegin+1 = 4 然后读入下个元素2, 注意,这个时候和情况2不同的是,不是if(A[k] > maxSum), 而是 if(A[k] + currentSum) { maxEnd = k+1; maxSum = A[k]+currentSum; } 这里,currentSum 的值是 1。
情况2 和 情况3 的唯一不同是: 在数组的迭代过程中,情况2在每次迭代的时候,都重置currentSum=0, 而情况3在读到元素1以后,就不重置currentSum了。还有,情况2中每读入一个新的元素,它都有可能是最大子数组的开始,但是在情况3中,元素2,3不可能是新的最大子数组的开始位置。--> 似乎我们可以得出规律:“如果上一次读到的元素是负数,我们属于情况2 (RESET状态):我们记录当前位置,并且在和maxSum比较以后,重置currentSum=0; 如果上一次读到的元素是正数,我们属于情况3(EXTEND状态): 我们不记录当前位置,和maxSum比较后不重置currentSum。
情况4: [ -5 -4 -1 1 2 3 -2 x y] 情况4 和情况3 在读到-2 之前是一样的,读到-2之前,最大和子数组是[1 2 3], 当前状态是EXTEND, currentSum = maxSum = 6, 我们注意到如果 x >=2, 我们就应该把[1 2 3]扩展到 [1 2 3 -2 x]; 如果 x < 2 , 我们还要看 x+y 是不是 >=2 ? hmm~~这样看似乎还是做不出判断,再仔细看看:
如果 currentSum + (-2) + x < 0 , 则 [1 2 3 -2 x] 一定不能扩展到 [1 2 3 -2 x y] 因为 currentSum = 1+2+3-2+x < 0 ,所以 y + currentSum < y !
如果 currentSum + (-2) + x + y > maxSum, 则 [1 2 3] 一定能扩展到 [ 1 2 3 -2 x y]。
所以: 情况4 应该总结成:
if( currentMode == EXTEND && currentSum + A[k] > maxSum) { maxSum = currentSum + A[k]; maxEnd = k+1; }
if( currentMode == EXTEND && currentSum + A[k] < 0 ) { currentMode = RESET, currentSum = 0; }
源代码:
public class MaxSumArray {
public static class Range {
int begin;
int end;
public Range() {
begin = 0;
end = 0;
}
}
public enum State {
RESET, EXTEND;
}
public static int getMaxSumArray(int[] array, Range range) {
State state = State.RESET;
int curBegin = 0;
int curEnd = curBegin + 1;
int curSum = 0;
int maxBegin = curBegin;
int maxEnd = curEnd;
int maxSum = array[0];
if (array[0] >= 0) {
curSum = array[0];
state = State.EXTEND;
}
for (int current = 1; current < array.length; current++) {
curSum += array[current];
if (state == State.RESET) {
// if in RESET, we treat every move as a possible range start
// so we need to remember this start position
curBegin = current;
if (curSum > maxSum) {
// we can safely abandon the previous max range under RESET mode
maxBegin = curBegin;
curEnd = current + 1;
// remember the new max range and value
maxEnd = curEnd;
maxSum = curSum;
}
// if we switch from RESET to EXTEND, we do NOT need to reset curSum to 0
if (curSum > 0) {
state = State.EXTEND;
}
if (state == State.RESET) {
curSum = 0;
}
} else { // if previous state is EXTEND
// under EXTEND mode, we do NOT need to change curBegin, neither maxBegin
if (curSum > maxSum) {
curEnd = current + 1;
// remember the new max range and value
maxEnd = curEnd;
maxSum = curSum;
}
// Extend
if (curSum < 0) {
state = State.RESET;
curSum = 0;
}
}
}// end of for
range.begin = maxBegin;
range.end = maxEnd;
return maxSum;
}
public static void print(int[] array, int maxSum, Range range) {
System.out.println("Subarray with maxSum = " + maxSum + " is :");
for (int i = range.begin; i < range.end; i++) {
System.out.print(array[i] + "/t");
}
System.out.println();
}
public static void main(String[] args) {
int[] given = { 1, -2, 3, 10, -4, 7, 2, -5 };
Range range = new Range();
int maxSum = getMaxSumArray(given, range);
print(given, maxSum, range);
int[] given2 = { -5, -2, -1, -3, -10 };
maxSum = getMaxSumArray(given2, range);
print(given2, maxSum, range);
}
}