前奏
希望更多的人能和我一樣,把本狂想曲系列中的任何一道面試題當做一道簡單的編程題或一個實質性的問題來看待,在閱讀本狂想曲系列的過程中,希望你能盡量暫時放下所有有關面試的一切包袱,潛心攻克每一道“編程題”,在解決編程題的過程中,好好享受編程帶來的無限樂趣,與思考帶來的無限激情。
第一節、求子數組的最大和
3.求子數組的最大和
題目描述:
輸入一個整形數組,數組裡有正數也有負數。
數組中連續的一個或多個整數組成一個子數組,每個子數組都有一個和。
求所有子數組的和的最大值。要求時間復雜度為O(n)。
例如輸入的數組為1, -2, 3, 10, -4, 7, 2, -5,和最大的子數組為3, 10, -4, 7, 2,
因此輸出為該子數組的和18。
分析:這個問題在各大公司面試中出現頻率之頻繁,被人引用次數之多,非一般面試題可與之匹敵。單憑這點,就沒有理由不入選狂想曲系列中了。此題曾作為本人之前整理的微軟100題中的第3題,至今反響也很大。ok,下面,咱們來一步一步分析這個題:
1、求一個數組的最大子數組和,如此序列1, -2, 3, 10, -4, 7, 2, -5,我想最最直觀也是最野蠻的辦法便是,三個for循環三層遍歷,求出數組中每一個子數組的和,最終求出這些子數組的最大的一個值。
記Sum[i, …, j]為數組A中第i個元素到第j個元素的和(其中0 <= i <= j < n),遍歷所有可能的Sum[i, …, j],那麼時間復雜度為O(N^3):
//本段代碼引自編程之美
int MaxSum(int* A, int n)
{
int maximum = -INF;
int sum;
for(int i = 0; i < n; i++)
{
for(int j = i; j < n; j++)
{
for(int k = i; k <= j; k++)
{
sum += A[k];
}
if(sum > maximum)
maximum = sum;
}
}
return maximum;
}
2、其實這個問題,在我之前上傳的微軟100題,答案V0.2版[第1-20題答案],便直接給出了以下O(N)的算法:view plaincopy to clipboardprint?
//copyright@ July 2010/10/18
//updated,2011.05.25.
#include <iostream.h>
int maxSum(int* a, int n)
{
int sum=0;
//其實要處理全是負數的情況,很簡單,如稍後下面第3點所見,直接把這句改成:"int sum=a[0]"即可
//也可以不改,當全是負數的情況,直接返回0,也不見得不行。
int b=0;
for(int i=0; i<n; i++)
{
if(b<=0) //把b<0改為 b<=0
b=a[i];
else
b+=a[i];
if(sum<b)
sum=b;
}
return sum;
}
int main()
{
int a[10]={1, -2, 3, 10, -4, 7, 2, -5};
//int a[]={-1,-2,-3,-4}; //測試全是負數的用例
cout<<maxSum(a,8)<<endl;
return 0;
}
/*-------------------------------------
解釋下:
例如輸入的數組為1, -2, 3, 10, -4, 7, 2, -5,
那麼最大的子數組為3, 10, -4, 7, 2,
因此輸出為該子數組的和18。
所有的東西都在以下倆行,
即:
b : 0 1 -1 3 13 9 16 18 7
sum: 0 1 1 3 13 13 16 18 18
其實算法很簡單,當前面的幾個數,加起來後,b<0後,
把b重新賦值,置為下一個元素,b=a[i]。
當b>sum,則更新sum=b;
若b<sum,則sum保持原值,不更新。。July、10/31。
----------------------------------*/
//copyright@ July 2010/10/18
//updated,2011.05.25.
#include <iostream.h>
int maxSum(int* a, int n)
{
int sum=0;
//其實要處理全是負數的情況,很簡單,如稍後下面第3點所見,直接把這句改成:"int sum=a[0]"即可
//也可以不改,當全是負數的情況,直接返回0,也不見得不行。
int b=0;
for(int i=0; i<n; i++)
{
if(b<=0) //把b<0改為 b<=0
b=a[i];
else
b+=a[i];
if(sum<b)
sum=b;
}
return sum;
}
int main()
{
int a[10]={1, -2, 3, 10, -4, 7, 2, -5};
//int a[]={-1,-2,-3,-4}; //測試全是負數的用例
cout<<maxSum(a,8)<<endl;
return 0;
}
/*-------------------------------------
解釋下:
例如輸入的數組為1, -2, 3, 10, -4, 7, 2, -5,
那麼最大的子數組為3, 10, -4, 7, 2,
因此輸出為該子數組的和18。
所有的東西都在以下倆行,
即:
b : 0 1 -1 3 13 9 16 18 7
sum: 0 1 1 3 13 13 16 18 18
其實算法很簡單,當前面的幾個數,加起來後,b<0後,
把b重新賦值,置為下一個元素,b=a[i]。
當b>sum,則更新sum=b;
若b<sum,則sum保持原值,不更新。。July、10/31。
----------------------------------*/
3、不少朋友看到上面的答案之後,認為上述思路2的代碼,沒有處理全是負數的情況,當全是負數的情況時,我們可以讓程序返回0,也可以讓其返回最大的那個負數,下面便是前幾日重寫的,修改後的處理全是負數情況(返回最大的負數)的代碼:
view plaincopy to clipboardprint?
//copyright@ July
//July、updated,2011.05.25。
#include <iostream.h>
#define n 4 //多定義了一個變量
int maxsum(int a[n])
//於此處,你能看到上述思路2代碼(指針)的優勢
{
int max=a[0]; //全負情況,返回最大數
int sum=0;
for(int j=0;j<n;j++)
{
if(sum>=0) //如果加上下一個,sum>=0的話,就加
sum+=a[j];
else
sum=a[j]; //如果加上下一個,sum<0了,就不加
if(sum>max)
max=sum;
}
return max;
}
int main()
{
int a[]={-1,-2,-3,-4};
cout<<maxsum(a)<<endl;
return 0;
}
//copyright@ July
//July、updated,2011.05.25。
#include <iostream.h>
#define n 4 //多定義了一個變量
int maxsum(int a[n])
//於此處,你能看到上述思路2代碼