1. 程式人生 > >遞迴呼叫分析-最大自序列求和問題

遞迴呼叫分析-最大自序列求和問題

昨天開始看《資料結構與演算法分析-java語言描述》這本書,在第二章舉例了一個演算法問題“最大子序列和問題”,在第三種遞迴方法,由於開始並沒有很好理解,遞迴呼叫在演算法中有很重要,用了最簡單一個例子來加深理解!(當然這種方法在這四種演算法中不是最優的)。


先給出原始碼:

package secondCHA;
 
public class maxSUM3 {
   
	static int MaxSubSum( int A[], int Left, int Right)  //問題?初始left和right是先給定的嗎?值是多少
	{  
	    int MaxLeftSum,MaxRightSum;  
	    int MaxLeftBorderSum,MaxRightBorderSum;  
	    int LeftBorderSum,RightBorderSum;  
	    int Center,i;  

		//遞迴終點,left>right的情況是不可能出現的,除非N是負數。
	    if(Left == Right)                              
	    {  
	        if(A[Left] > 0)  
	            return A[Left];  
	        else  
	            return 0;  
	    }  
	      
	    Center = (Left + Right)/2;  
	    //重點,遞迴部分
		MaxLeftSum = MaxSubSum(A,Left,Center);  
	    MaxRightSum = MaxSubSum(A,Center+1,Right);  
	     

		// 計算left達到中間分界處的兩個最大和的和數
	    MaxLeftBorderSum = 0;  
	    LeftBorderSum = 0;  
	    for(i = Center;i >= Left;i--)  
	    {  
	        LeftBorderSum += A[i];  
	        if(LeftBorderSum > MaxLeftBorderSum)  
	            MaxLeftBorderSum = LeftBorderSum;  
	    }  
	    
		// 計算right達到中間分界處的兩個最大和的和數
	    MaxRightBorderSum = 0;  
	    RightBorderSum = 0;  
	    for(i = Center+1;i <= Right;i++)  
	    {  
	        RightBorderSum += A[i];  
	        if(RightBorderSum > MaxRightBorderSum)  
	            MaxRightBorderSum = RightBorderSum;  
	    }     
	      
	    return Max(MaxLeftSum,MaxRightSum,MaxLeftBorderSum + MaxRightBorderSum);  
	}   
	  
	static int Max(int a, int b, int c)  
	{  
	    if(a>b&&a>c)  
	        return a;  
	    else if(b>a&&b>c)  
	        return b;  
	    else  
	        return c;   
	}  
	public static void main(String[] args) { 
		// TODO Auto-generated method stub
		 int[] a={4,-3,5,-2,-1,2,6,-2};
		 int left=0;
		 int right=a.length-1;
		 System.out.println(MaxSubSum(a,left,right));
	}
 
}

用8個元素的陣列進行分析:

A0

A1

A2

A3

A4

A5

A6

A7

4

-3

5

-2

-1

2

6

-2

第一次:MaxSubSum(A,0,7)

A0

A1

A2

A3

A4

A5

A6

A7

4

-3

5

-2

-1

2

6

-2

Int center=(0+7)/2=3

左邊(A0-A3)部分包含最後一個元素的最大值為:

LeftBorderSum=Max(A3,A3+A2,A3+A2+A1,A3+A2+A1+A0)=4

右邊(A4-A7)部分包含第一個元素的最大值為:

RightBorderSum=Max(A4,A4+A5,A4+A5+A6,A4+A5+A6+A7)=7

return Max(MaxLeftSum,MaxRightSum,MaxLeftBorderSum + MaxRightBorderSum=

11);

MaxSubSum(A,0,7)=return Max(MaxSubSum(A,0,3),MaxSubSum(A,4,7),11);

第一次的返回值採用了中MaxLeftSum = MaxSubSum(A,0,3)和MaxRightSum = MaxSubSum(A,4,7)採用了遞迴,則需先確定MaxSubSum(A,0,3)和MaxSubSum(A,4,7),因為引出了第二次分析。

 

第二次MaxSubSum(A,0,3),MaxSubSum(A,4,7)

1  MaxSubSum(A,0,3)

A0

A1

A2

A3

4

-3

5

-2

Int center=(0+3)/2=1

左邊(A0-A1)部分包含最後一個元素的最大值為:

LeftBorderSum=Max(A1,A1+A0,)=1

右邊(A2-A3)部分包含第一個元素的最大值為:

RightBorderSum=Max(A2,,A2+A3)=5

return Max(MaxLeftSum,MaxRightSum,MaxLeftBorderSum + MaxRightBorderSum=6);

MaxSubSum(A,0,3)=return Max(MaxSubSum(A,0,1),MaxSubSum(A,2,3),6);

2 MaxSubSum(A,4,7)

A4

A5

A6

A7

-1

2

6

-2

Int center=(4+7)/2=5

左邊(A4-A5)部分包含最後一個元素的最大值為:

LeftBorderSum=Max(A5,A5+A4)=2

右邊(A6-A7)部分包含第一個元素的最大值為:

RightBorderSum=Max(A6,A6+A7)=6

return Max(MaxLeftSum,MaxRightSum,MaxLeftBorderSum + MaxRightBorderSum=8);

MaxSubSum(A,4,7)=return Max(MaxSubSum(A,4,5),MaxSubSum(A,6,7),8);

 

第三次MaxSubSum(A,0,1),MaxSubSum(A,2,3),MaxSubSum(A,4,5),MaxSubSum(A,6,7)

1.1 MaxSubSum(A,0,1)

A0

A1

4

-3

MaxSubSum(A,0,1)=return Max(MaxSubSum(A,0,0),MaxSubSum(A,1,1),1);

1.2 MaxSubSum(A,2,3)

A2

A3

5

-2

MaxSubSum(A,2,3)=return Max(MaxSubSum(A,2,2),MaxSubSum(A,3,3),3);

2.1 MaxSubSum(A,4,5)

A4

A5

-1

2

MaxSubSum(A,4,5)=return Max(MaxSubSum(A,4,4),MaxSubSum(A,5,5),1);

2.2 MaxSubSum(A,6,7)

A6

A7

6

-2

MaxSubSum(A,6,7)=return Max(MaxSubSum(A,6,6),MaxSubSum(A,7,7),4);

 

第四次MaxSubSum(A,0,0),MaxSubSum(A,1,1),MaxSubSum(A,2,2),MaxSubSum(A,3,3),MaxSubSum(A,4,4),MaxSubSum(A,5,5),MaxSubSum(A,6,6),MaxSubSum(A,7,7)

這時到達遞迴終點,由left=right

    if(Left == Right)                              

    {  

        if(A[Left] > 0)  

            return A[Left];  

        else  

            return 0;  

    }  

因此:MaxSubSum(A,0,0)=4,MaxSubSum(A,1,1)=0,MaxSubSum(A,2,2)=5,MaxSubSum(A,3,3)=0,MaxSubSum(A,4,4)=0,MaxSubSum(A,5,5)=2,MaxSubSum(A,6,6)=6,MaxSubSum(A,7,7)=0

遞迴結束,則將遞迴終點按第四次-第三次-第二次-第一次返回。

MaxSubSum(A,0,0)=4,MaxSubSum(A,1,1)=0,MaxSubSum(A,2,2)=5,MaxSubSum(A,3,3)=0,MaxSubSum(A,4,4)=0,MaxSubSum(A,5,5)=2,MaxSubSum(A,6,6)=6,MaxSubSum(A,7,7)=0.

 

MaxSubSum(A,0,1)=return Max(MaxSubSum(A,0,0),MaxSubSum(A,1,1),1)=4

MaxSubSum(A,2,3)=return Max(MaxSubSum(A,2,2),MaxSubSum(A,3,3),3)=5

MaxSubSum(A,4,5)=return Max(MaxSubSum(A,4,4),MaxSubSum(A,5,5),1)=2;

MaxSubSum(A,6,7)=return Max(MaxSubSum(A,6,6),MaxSubSum(A,7,7),4)=6

 

MaxSubSum(A,0,3)=return Max(MaxSubSum(A,0,1),MaxSubSum(A,2,3),6)=6

MaxSubSum(A,4,7)=return Max(MaxSubSum(A,4,5),MaxSubSum(A,6,7),8)=8

 

MaxSubSum(A,0,7)=return Max(MaxSubSum(A,0,3),MaxSubSum(A,4,7),11)=11

 

總結:在理解遞迴的時候可以把他看做一個黑盒子,採用歸納證明發的思想,重點關注遞迴終點和遞迴方法。例如本例子中,這個方法的思想是,最大子序列可能有三種情況:左邊,右邊和包含左右挨著的元素組。就可以求左邊最大,右邊最大,左右同時包含最大,這三者中的最大值,其中左右同時包含的最大值是可以求得的,因此只需求左最大和右最大,這裡又可以把左最大和右最大看成2個獨立元素組......遞迴迴圈。遞迴終點,就是進入死迴圈或者無法繼續,這時當left=right時,就需要終點程式。