最大和子數組/最大和子序列


  最大和子數組是數組中和最大的子數組,又名最大和子序列。子數組是數組中連續的n個元素,比如a2,a3,a4就是一個長度為3的子數組。顧名思義求最大和子數組就是要求取和最大的子數組。

  

  n個元素的數組包含n個長度為1的子數組:{a0},{a1},…{an-1};

  n個元素的數組包含n-1個長度為2的子數組:{a0,a1},{a1,a2},{an-2,an-1};

  ………………………………………………………………………………………………

  n個元素的數組包含1個長度為n的子數組:{a0,a1,…,an-1};

  所以,一個長度為n的數組包含的子數組個數為n+(n-1)+…+1=n*(n-1)/2。

  第一反應就是蠻力法,窮舉數組所有子數組的和並求出和的最大值。這種方法簡單直觀容易想到,具體又有幾種不同的思路。第一種方法是先求出所有長度為1的子數組的和,再求長度為2的子數組的和,以此類推,直到求出所有長度為n的子數組的和,並在求子數組和的過程中記錄和的最大值。偽代碼如下:

maxSum=arr[0];//maxSum記錄最大子數組和

for(i=1,i<=n;i++){//子數組長度

  for(j=0;j<n;j++){//子數組開始的位置(數組下標)

    sum=0;//sum記錄當前子數組和

    for(k=j;k<n&&k<j+i;k++){//求和

      sum+=arr[k];

    }

    if(sum>maxSum) maxSum=sum;

  }

}

  第二種方法是先求所有以a[0]開始的子數組的和,再求所有以a[1]開始的子數組的和,以此類推,直到最后求出所有以a[n-1]開始的子數組的和,並在求子數組和的過程中記錄和的最大值。偽代碼如下:

int maxSubArraySum(int *arr,int n){

  int i,j,maxSum=arr[0],sum;

  for(i=0;i<n;i++){//子數組開始位置

    sum=0;

    for(j=i;j<n;j++){//以arr[i]開始的不同長度的子數組的和,求和是一個遞進過程

      sum+=arr[j];

      if(sum>maxSum) maxSum=sum;

    }    

  }

  return maxSum;

}

  蠻力法雖然可以求得問題的解,但蠻力法通常不是我們所希望的,其時間復雜度大。我們希望找到一 種更有效的方法來解決該問題,所以需要思考問題具有的特征,拿到問題就開始寫代碼是最忌諱的,代碼前需要三思。最大和子數組一定以數組中的某個元素a[i](0<=i<n)結束,那么我們可以先分別求出以每個元素結尾的子數組的最大和,其中的最大值就是所求的最大子數組和。后一個元素的求和需要用到前一個元素的結果,遞推公式為maxSumEnd[i]=max{maxSumEnd[i-1]+a[i],a[i]},代碼如下:

int maxSumArraySumD(int *arr,int n){
  int i,maxSum=arr[0];
  int *maxSumEnd;
  maxSumEnd=(int*)malloc(sizeof(int)*n);
  maxSumEnd[0]=arr[0];
  for(i=1;i<n;i++){
    if(maxSumEnd[i-1]+arr[i]>arr[i])

      maxSumEnd[i]=maxSumEnd[i-1]+arr[i];
    else

      maxSumEnd[i]=arr[i];

    if(maxSumEnd[i]>maxSum)

      maxSum=maxSumEnd[i];
  }
  free(maxSumEnd);
  return maxSum;
}

  這道題目如果我們能聯想到一些數學知識就可以得到更簡潔的答案,優秀的程序猿一定具有很厚的數學基礎和敏捷的數學思維,但有很厚數學基礎和敏捷數學思維的人不一定就是程序猿,歷史證明無數優秀的數學家都變成了經濟學家,計算機這個專業學到后面也就剩英語和數學了,華爾街那幫吸血鬼個個都是數學怪才。

  一個數加上一個負數和會變小,一個數加上0和保持不變,一個數只有加上一個正數和才會變大。如果最大和子數組為a[i],a[j],a[k],那么一定有a[i]+a[j]>0,如果a[i]+a[j]<=0,那么最大和子數組就是a[k]。因此,我們可以從a[0]累加求和,只要累加的和大於0就繼續向后累加,如果累加的和小於0,那就舍棄掉,從下一個元素從新開始累加,並在累加過程中記錄和的最大值。代碼如下:

int maxSubArraySum(int *arr,int n){
  int i,maxSum=arr[0],sum=0;
  for(i=0;i<n;i++){
    sum+=arr[i];
    if(sum>maxSum){ //記錄最大累加和
      maxSum=sum;
    }
    if(sum<0){//累加和小於0舍棄
      sum=0;
    }
  }
  return maxSum;
}

  或許還需給出最大和子數組,那么要在求最大和子數組的過程中記錄最大和子數組的起始位置。代碼如下:

int maxSubArraySumPos(int *arr,int n){
  int i,maxSum=arr[0],sum=0;
  int start=0,end=0,s=0,e=0;
  for(i=0;i<n;i++){
    sum+=arr[i];
    if(sum>maxSum){
      maxSum=sum;
      e=i;
      start=s;
      end=e;
    }
    if(sum<0){
      sum=0;
      s=i+1;
      e=i+1;
    }
  }
  printf("start=%d end=%d\n",start,end);
  return maxSum;
}

  羅馬之路不順暢,大俠們不要吝嗇自己的武功秘籍,評論區為各位大俠所留。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM