劍指Offer面試題:連續子數組的最大和

題目:輸入一個整型數組,數組裏有正數也有負數。數組中一個或連續的多個整數組成一個子數組。面試

            求全部子數組的和的最大值。要求時間複雜度爲O(n)編程

例如輸入的數組爲{1,-2,3,10,-4,7,2,-5}數組

看到該題目,不少人都能想到最直觀的方法,即枚舉出數組的全部子數組並求出他們的和。一個長度爲n的數組,總共有n(n+1)/2個子數組。計算出全部的子數組的和,最快也須要O(n2)的時間。一般最直觀的方法不會是最優的方法,面試官將提示咱們還有更快的方法。函數

 

解法一:舉例分析數組的規律:編碼

咱們試着從頭尾逐個累加示例數組中的每一個數字。初始化和爲0.第一步 加上第一個數字,此時和爲1.接下來第二步加上數字-2,和就編程了-1.第三步加上數字3.咱們注意到因爲此前累計的和爲-1,小於0,那若是用-1加 3,獲得的和爲2,比3自己還小。也就是說從第一個數字開始的子數組的和會小於從第三個數字開始的子數組的和。所以咱們不用考慮從第一個子數組,以前累計 的和也被拋棄。.net

咱們從第三個數字從新開始累加,此時獲得的和爲3.接下來第四步加 10,獲得和爲13.第五步加上-4,和爲9.咱們發現-4是一個負數,所以累加-4以後獲得的和比原來的還小。所以咱們要把以前獲得的和13保存下來, 它有多是最大的子數組的和。第六步加上數字7,9加7的結果是16,此時和比以前最大的和13還要大,把最大的子數組的和由13更新爲16.第七步加上 2,累加獲得的和爲18,同時咱們也要更新最大子數組的和。第八步加上最後一個數字-5,因爲獲得結果爲13,小於此前獲得的最大和18,所以最終最大的 子數組的和爲18,對應的子數組是{3,10,-4,7,2}。遞歸

把過程分析清楚以後,咱們用Java代碼實現:get

package cglib;class

public class jiekou {test

    public int findGreatestSubArray(int[] array){
        if(array==null||array.length<0)
        return 0;
        
        int currentSum=0;
        int greatestSum=0;
        for(int i=0;i<array.length;i++)
        {
            System.out.println("array[i]:"+array[i]);
            System.out.println("currentSum:"+currentSum);
            if(currentSum<=0){
            System.out.println("currentSum<=0");
            currentSum=array[i];
            System.out.println("賦值後:currentSum:"+currentSum);
        }else{
            System.out.println("currentSum>0");
            currentSum+=array[i];
            System.out.println("相加後:currentSum:"+currentSum);
        } if(currentSum>greatestSum){
            System.out.println("currentSum>greatestSum");
            System.out.println("currentSum:"+currentSum);
            System.out.println("greatestSum:"+greatestSum);
            greatestSum=currentSum;
            System.out.println("賦值後greatestSum:"+greatestSum);
        }
        }
        System.out.println("返回greatestSum:"+greatestSum);
        return greatestSum;
        }
      
    /**
     * @param args
     */  
    public static void main(String[] args) {
        jiekou p=new jiekou();
        int[] array={1,-2,3,10,-4,7,2,-5};
        System.out.println(p.findGreatestSubArray(array));
        }
    }   


輸出:

array[i]:1
currentSum:0
currentSum<=0
賦值後:currentSum:1
currentSum>greatestSum
currentSum:1
greatestSum:0
賦值後greatestSum:1
array[i]:-2
currentSum:1
currentSum>0
相加後:currentSum:-1
array[i]:3
currentSum:-1
currentSum<=0
賦值後:currentSum:3
currentSum>greatestSum
currentSum:3
greatestSum:1
賦值後greatestSum:3
array[i]:10
currentSum:3
currentSum>0
相加後:currentSum:13
currentSum>greatestSum
currentSum:13
greatestSum:3
賦值後greatestSum:13
array[i]:-4
currentSum:13
currentSum>0
相加後:currentSum:9
array[i]:7
currentSum:9
currentSum>0
相加後:currentSum:16
currentSum>greatestSum
currentSum:16
greatestSum:13
賦值後greatestSum:16
array[i]:2
currentSum:16
currentSum>0
相加後:currentSum:18
currentSum>greatestSum
currentSum:18
greatestSum:16
賦值後greatestSum:18
array[i]:-5
currentSum:18
currentSum>0
相加後:currentSum:13
返回greatestSum:18
18

解法二:應用動態規劃法

 

咱們還能夠適用動態規劃的思想來分析這個問題。若是用函數f(i)表示以第i個數字結尾的子數組的最大和,那麼咱們須要求出max[f(i)],其中0<=i<n。咱們可使用可以下面的遞歸公示求f(i):

這個公式的意義:當以第i-1個數字結尾的子數組中全部的數字的和小 於0時,若是把這個負數與第i個數累加,獲得的結果比第i個數字自己還要小,因此這種狀況下以第i個數字結尾的子數組就是第i個數字自己。若是以第i-1 個數字結尾的子數組中全部的數字的和大於0,與第i個數字累加就獲得以第i個數字結尾的子數組中全部的數字的和。

雖然咱們用遞歸的方法分析動態規劃的問題,但最終都會基於循環去編碼。

  1. 使用動態規劃方法來實現: 
  2. *若是用函數f(i)表示以第i個數字結尾的子數組的最大和,那麼咱們須要求出max(f[0...n])。 
  3. *咱們能夠給出以下遞歸公式求f(i) 
  4. *     |-- array[i] 若是i==0或者f(i-1)<0 
  5. *f(i)=| 
  6. *     |-- f(i-1) + array[i] 若是f(i-1)>0 
  7. *這個公式的意義: 
  8. *   當以第(i-1)個數字爲結尾的子數組中全部數字的和f(i-1)小於0時,若是把這個負數和第i個數相加,獲得的結果反而比第i個數自己還要小,因此這種狀況下最大子數組和是第i個數自己。 
  9. *   若是以第(i-1)個數字爲結尾的子數組中全部數字的和f(i-1)大於0,與第i個數累加就獲得了以第i個數結尾的子數組中全部數字的和。

package cglib;

public class jiekou {

    public int findGreatestSubArray(int[] array){
        if(array==null||array.length<0)
        return 0;
        
        int[] c=new int[array.length];//用來記錄以當前元素結尾(數組就到當前元素的位置爲止)的子數組的最大和  
        int max = -1000;//用來記錄數組c[]中的最大值  
        int start = 0;//記錄數組中子數組的最大和的開始位置  
        int end = 0;//記錄數組中子數組的最大和的結束位置  
        int tmp = 0;  
      
        c[0] = array[0];
        System.out.println("c[0]:"+c[0]);
        for(int i = 1;i < array.length;++i)  
        {   
            System.out.println("array[i]:"+array[i]);
            System.out.println("c[i-1]:"+c[i-1]);
            if(c[i-1] > 0)  
            {   System.out.println("c[i-1]大於0");
                c[i] = c[i-1] + array[i];
                System.out.println("c[i]:"+c[i]);
            }  
            else  
            {   
                System.out.println("c[i-1]小於0");
                c[i] = array[i];
                System.out.println("c[i]:"+c[i]);
                tmp = i;
                System.out.println("tmp:"+tmp);
            }  
            if(c[i] > max)  
            {   
                System.out.println("c[i]大於max");
                max = c[i];  
                start = tmp;  
                end = i;  
            }  
        }  
        System.out.println("子數組最大和的起始位置:"+start+"~"+end);
        System.out.println("最大值max:"+max);
        return max;
        
    }
      
    /**
     * @param args
     */  
    public static void main(String[] args) {
        jiekou p=new jiekou();
        int[] array={1,-2,3,10,-4,7,2,-5};
        System.out.println(p.findGreatestSubArray(array));
        }
    }   


輸出:
 

c[0]:1 array[i]:-2 c[i-1]:1 c[i-1]大於0 c[i]:-1 c[i]大於max array[i]:3 c[i-1]:-1 c[i-1]小於0 c[i]:3 tmp:2 c[i]大於max array[i]:10 c[i-1]:3 c[i-1]大於0 c[i]:13 c[i]大於max array[i]:-4 c[i-1]:13 c[i-1]大於0 c[i]:9 array[i]:7 c[i-1]:9 c[i-1]大於0 c[i]:16 c[i]大於max array[i]:2 c[i-1]:16 c[i-1]大於0 c[i]:18 c[i]大於max array[i]:-5 c[i-1]:18 c[i-1]大於0 c[i]:13 子數組最大和的起始位置:2~6 最大值max:18 18

相關文章
相關標籤/搜索