題目:要求一個數組連續下標和的最大值,數組的元素可正、可負、可為零,例如-2,5,3,-6,4,-8,6將返回8。
這題是很經典的一道面試題,也有各種解法,從算法分析上,時間復雜度也有很大差別,下面我就給出三種不同的解法。
方法一:暴力枚舉法
此種方法最簡單,我想應該也是每個人拿到題目想到的第一種解法了,學過一點編程的人都應該能編出此類程序。
記sum[i..j]為數組中第i個元素到第j個元素的和(其中0<=i<j<=n-1),通過遍歷所有的組合之和,就能找到最大的一個和了。
偽代碼如下:
方法二:分支界定
這里再介紹一種更高效的算法,時間 復雜度為O(nlogn)。這是個分治的思想,解決復雜問題我們經常使用的一種思維方法——分而治之。
而對於此題,我們把數組A[1..n]分成兩個相等大小的塊:A[1..n/2]和A[n/2+1..n],最大的子數組只可能出現在三種情況:
A[1..n]的最大子數組和A[1..n/2]最大子數組相同;
A[1..n]的最大子數組和A[n/2+1..n]最大子數組相同;
A[1..n]的最大子數組跨過A[1..n/2]和A[n/2+1..n]
前兩種情況的求法和整體的求法是一樣的,因此遞歸求得。
第三種,我們可以采取的方法也比較簡單,沿着第n/2向左搜索,直到左邊界,找到最大的和maxleft,以及沿着第n/2+1向右搜索找到最大和maxright,那么總的最大和就是maxleft+maxright。
而數組A的最大子數組和就是這三種情況中最大的一個。
偽代碼如下:
這算是一個經典的動態規划的題目了,如果不知道動態規划可以先不去理解這個名詞。用通俗點的語言描述這個算法就是:
令cursum(i)表示數組下標以i為起點的最大連續下標最大的和,而maxsum(i)表示前i個元素的最大子數組之和。那么我們就可以推出下一個maxsum(i+1)應該為cursum(i+1)和maxsum(i)中選取一個最大值。遞推式為:
cursum(i) = max{A[i],cursum(i-1)+A[i]};
maxsum(i) = max{maxsum(i-1),cursum(i+1)};
偽代碼為:
具體實現代碼如下:
這題是很經典的一道面試題,也有各種解法,從算法分析上,時間復雜度也有很大差別,下面我就給出三種不同的解法。
方法一:暴力枚舉法
此種方法最簡單,我想應該也是每個人拿到題目想到的第一種解法了,學過一點編程的人都應該能編出此類程序。
記sum[i..j]為數組中第i個元素到第j個元素的和(其中0<=i<j<=n-1),通過遍歷所有的組合之和,就能找到最大的一個和了。
偽代碼如下:
int maxSubArray(int *A,int n) {此種方法的時間 復雜度為O(n2),顯然不是一種很好的辦法,也不是公司面試希望你寫出這樣的程序的。
int maxium = -INF; //保存最大子數組之和
for i=0 to n-1 do
sum = 0; //sum記錄第i到j的元素之和
for j=i to n-1 do
sum += A[j];
if sum>maxium do //更新最大值
maxium = sum;
return maxium;
}
方法二:分支界定
這里再介紹一種更高效的算法,時間 復雜度為O(nlogn)。這是個分治的思想,解決復雜問題我們經常使用的一種思維方法——分而治之。
而對於此題,我們把數組A[1..n]分成兩個相等大小的塊:A[1..n/2]和A[n/2+1..n],最大的子數組只可能出現在三種情況:
A[1..n]的最大子數組和A[1..n/2]最大子數組相同;
A[1..n]的最大子數組和A[n/2+1..n]最大子數組相同;
A[1..n]的最大子數組跨過A[1..n/2]和A[n/2+1..n]
前兩種情況的求法和整體的求法是一樣的,因此遞歸求得。
第三種,我們可以采取的方法也比較簡單,沿着第n/2向左搜索,直到左邊界,找到最大的和maxleft,以及沿着第n/2+1向右搜索找到最大和maxright,那么總的最大和就是maxleft+maxright。
而數組A的最大子數組和就是這三種情況中最大的一個。
偽代碼如下:
int maxSubArray(int *A,int l,int r) {方法三:動態規划
if l<r do
mid = (l+r)/2;
ml = maxSubArray(A,l,mid); //分治
mr = maxSubArray(A,mid+1,r);
for i=mid downto l do
search maxleft;
for i=mid+1 to r do
search maxright;
return max(ml,mr,maxleft+maxright); //歸並
then //遞歸出口
return A[l];
}
這算是一個經典的動態規划的題目了,如果不知道動態規划可以先不去理解這個名詞。用通俗點的語言描述這個算法就是:
令cursum(i)表示數組下標以i為起點的最大連續下標最大的和,而maxsum(i)表示前i個元素的最大子數組之和。那么我們就可以推出下一個maxsum(i+1)應該為cursum(i+1)和maxsum(i)中選取一個最大值。遞推式為:
cursum(i) = max{A[i],cursum(i-1)+A[i]};
maxsum(i) = max{maxsum(i-1),cursum(i+1)};
偽代碼為:
int maxSubArray(int *A,int n) {這種算法時間復雜度只是O(n),效果非常好!
cursum = A[0];
maxsum = A[0];
for i=1 to n-1 do
/*當我們加上一個正數時,和會增加;當我們加上一個負數時,和會減少。如果當前得到的和是個負數,那么這個和在接下來的累加中應該拋棄並重新清零,不然的話這個負數將會減少接下來的和。*/
if cursum<0 do
cursum = 0;
cursum += A[i];
if cursum>maxsum do
maxsum = cursum;
return maxsum;
}
具體實現代碼如下:
#include <iostream>
#include <iterator>
#include <algorithm>
#include <cstdlib>
#include <ctime>
using namespace std;
const int INF= 0x7fffffff;
int max_sub_array( int arr[], int n, int &left, int &right)
{
int maxium=-INF;
int sum;
for( int i= 0;i<n;i++){
sum= 0;
for( int j=i;j<n;j++){
sum+=arr[j];
if(sum>maxium){
maxium=sum;
left=i;
right=j;
}
}
}
return maxium;
}
int max_sub_array( int arr[], int l, int r, int &left, int &right)
{
if(l<r){
int mid=(l+r)/ 2;
int ll,lr;
int suml=max_sub_array(arr,l,mid,ll,lr);
int rl,rr;
int sumr=max_sub_array(arr,mid+ 1,r,rl,rr);
int sum_both= 0;
int max_left=-INF;
int ml,mr;
for( int i=mid;i>=l;i--)
{
sum_both+=arr[i];
if(sum_both>max_left){
max_left=sum_both;
ml=i;
}
}
int max_right=-INF;
sum_both= 0;
for( int i=mid+ 1;i<=r;i++)
{
sum_both+=arr[i];
if(sum_both>max_right){
max_right=sum_both;
mr=i;
}
}
sum_both=max_left+max_right;
if(suml<sumr) {
if(sumr<sum_both) {
left=ml;
right=mr;
return sum_both;
}
else {
left=rl;
right=rr;
return sumr;
}
}
else{
if(suml<sum_both) {
left=ml;
right=mr;
return sum_both;
}
else {
left=ll;
right=lr;
return suml;
}
}
}
else {
left=l;
right=r;
return arr[l];
}
}
int max_sub_array_( int arr[], int n, int& left, int&right)
{
int cursum=arr[ 0];
int maxsum=arr[ 0];
int pos= 0;
pos= 0;
for( int i= 1;i<n;i++) {
// if(cursum<0)
// cursum=0;
cursum+=arr[i];
if(cursum<arr[i])
{
pos=i;
cursum=arr[i];
}
if(cursum>maxsum)
{
maxsum=cursum;
left=pos;
right=i;
}
}
return maxsum;
}
void test1()
{
int arr[]={- 2, 5, 3,- 6, 4,- 8, 6};
int len= sizeof(arr)/ sizeof(arr[ 0]);
int left,right;
int sum;
cout<< " arr: ";
copy(arr,arr+len,ostream_iterator< int>(cout, " "));
cout<<endl;
sum=max_sub_array(arr,len,left,right);
cout<< " method1:( "<<left<< " , "<<right<< " ) ";
cout<< " sum= "<<sum<<endl;
sum=max_sub_array(arr, 0,len- 1,left,right);
cout<< " method2:( "<<left<< " , "<<right<< " ) ";
cout<< " sum= "<<sum<<endl;
sum=max_sub_array(arr,len,left,right);
cout<< " method3:( "<<left<< " , "<<right<< " ) ";
cout<< " sum= "<<sum<<endl;
}
void test2()
{
const int LEN= 10;
int arr[LEN];
int sign[LEN];
srand(time( 0));
for( int i= 0;i<LEN;i++){
int val=rand()% 1000;
if(val% 2== 0)
sign[i]= 1;
else
sign[i]=- 1;
}
for( int i= 0;i<LEN;i++){
int val=rand()% 100;
arr[i]=val*sign[i];
}
int left,right;
int sum;
int len=LEN;
cout<< " arr: ";
copy(arr,arr+len,ostream_iterator< int>(cout, " "));
cout<<endl;
sum=max_sub_array(arr,len,left,right);
cout<< " method1:( "<<left<< " , "<<right<< " ) ";
cout<< " sum= "<<sum<<endl;
sum=max_sub_array(arr, 0,len- 1,left,right);
cout<< " method2:( "<<left<< " , "<<right<< " ) ";
cout<< " sum= "<<sum<<endl;
sum=max_sub_array(arr,len,left,right);
cout<< " method3:( "<<left<< " , "<<right<< " ) ";
cout<< " sum= "<<sum<<endl;
}
int main()
{
test2();
}
#include <iterator>
#include <algorithm>
#include <cstdlib>
#include <ctime>
using namespace std;
const int INF= 0x7fffffff;
int max_sub_array( int arr[], int n, int &left, int &right)
{
int maxium=-INF;
int sum;
for( int i= 0;i<n;i++){
sum= 0;
for( int j=i;j<n;j++){
sum+=arr[j];
if(sum>maxium){
maxium=sum;
left=i;
right=j;
}
}
}
return maxium;
}
int max_sub_array( int arr[], int l, int r, int &left, int &right)
{
if(l<r){
int mid=(l+r)/ 2;
int ll,lr;
int suml=max_sub_array(arr,l,mid,ll,lr);
int rl,rr;
int sumr=max_sub_array(arr,mid+ 1,r,rl,rr);
int sum_both= 0;
int max_left=-INF;
int ml,mr;
for( int i=mid;i>=l;i--)
{
sum_both+=arr[i];
if(sum_both>max_left){
max_left=sum_both;
ml=i;
}
}
int max_right=-INF;
sum_both= 0;
for( int i=mid+ 1;i<=r;i++)
{
sum_both+=arr[i];
if(sum_both>max_right){
max_right=sum_both;
mr=i;
}
}
sum_both=max_left+max_right;
if(suml<sumr) {
if(sumr<sum_both) {
left=ml;
right=mr;
return sum_both;
}
else {
left=rl;
right=rr;
return sumr;
}
}
else{
if(suml<sum_both) {
left=ml;
right=mr;
return sum_both;
}
else {
left=ll;
right=lr;
return suml;
}
}
}
else {
left=l;
right=r;
return arr[l];
}
}
int max_sub_array_( int arr[], int n, int& left, int&right)
{
int cursum=arr[ 0];
int maxsum=arr[ 0];
int pos= 0;
pos= 0;
for( int i= 1;i<n;i++) {
// if(cursum<0)
// cursum=0;
cursum+=arr[i];
if(cursum<arr[i])
{
pos=i;
cursum=arr[i];
}
if(cursum>maxsum)
{
maxsum=cursum;
left=pos;
right=i;
}
}
return maxsum;
}
void test1()
{
int arr[]={- 2, 5, 3,- 6, 4,- 8, 6};
int len= sizeof(arr)/ sizeof(arr[ 0]);
int left,right;
int sum;
cout<< " arr: ";
copy(arr,arr+len,ostream_iterator< int>(cout, " "));
cout<<endl;
sum=max_sub_array(arr,len,left,right);
cout<< " method1:( "<<left<< " , "<<right<< " ) ";
cout<< " sum= "<<sum<<endl;
sum=max_sub_array(arr, 0,len- 1,left,right);
cout<< " method2:( "<<left<< " , "<<right<< " ) ";
cout<< " sum= "<<sum<<endl;
sum=max_sub_array(arr,len,left,right);
cout<< " method3:( "<<left<< " , "<<right<< " ) ";
cout<< " sum= "<<sum<<endl;
}
void test2()
{
const int LEN= 10;
int arr[LEN];
int sign[LEN];
srand(time( 0));
for( int i= 0;i<LEN;i++){
int val=rand()% 1000;
if(val% 2== 0)
sign[i]= 1;
else
sign[i]=- 1;
}
for( int i= 0;i<LEN;i++){
int val=rand()% 100;
arr[i]=val*sign[i];
}
int left,right;
int sum;
int len=LEN;
cout<< " arr: ";
copy(arr,arr+len,ostream_iterator< int>(cout, " "));
cout<<endl;
sum=max_sub_array(arr,len,left,right);
cout<< " method1:( "<<left<< " , "<<right<< " ) ";
cout<< " sum= "<<sum<<endl;
sum=max_sub_array(arr, 0,len- 1,left,right);
cout<< " method2:( "<<left<< " , "<<right<< " ) ";
cout<< " sum= "<<sum<<endl;
sum=max_sub_array(arr,len,left,right);
cout<< " method3:( "<<left<< " , "<<right<< " ) ";
cout<< " sum= "<<sum<<endl;
}
int main()
{
test2();
}
其中test1函數是題目給出的數組測試,test2是進行隨機產生數來測試。
大致的效果如下:
test1測試:
參考:http://www.cnblogs.com/hazir/archive/2011/05/08/2447289.html