動態規划求解最長遞增子序列的長度


一,問題描述

給定一個序列,求解它的最長 遞增 子序列 的長度。比如: arr[] = {3,1,4,1,5,9,2,6,5}   的最長遞增子序列長度為4。即為:1,4,5,9

 

二,算法分析

有兩種方式來求解,一種是轉化為LCS問題。即,首先對數組排序,將排序后的結果存儲在輔助數組中。排序時間復雜度O(NlogN),排序后的數組與原數組組成了LCS(N,N)問題。解決LCS問題的時間復雜度為O(N^2),故整個算法的時間復雜度為O(N^2),空間復雜度為O(N)

 

另一種方式是直接用DP求解,算法如下:時間復雜度為O(N^2)

①最優子問題

設lis[i] 表示索引為 [0...i] 上的數組上的 最長遞增子序列。初始時,lis[i]=1,注意,在DP中,初始值是很重要的,它是整個算法運行正確的關鍵而初始值 則可以 通過 畫一個小的示例來 確定。

當 arr[i] > arr[j],lis[i] = max{lis[j]}+1 ;其中,j 的取值范圍為:0,1...i-1

當 arr[i] < arr[j],lis[i] = max{lis[j]} ;其中,j 的取值范圍為:0,1...i-1

 

②重疊子結構

從上面可以看出,計算 lis[i]時,需要計算 lis[j],其中 j < i,這說明有重疊子問題。借用網路中一張圖如下:

                     lis(4)           
                 /       |      \
         lis(3)      lis(2)    lis(1)  
        /     \        /         
  lis(2)  lis(1)   lis(1) 
  /    
lis(1)

而初始值 則可以 通過 畫一個小的示例來 確定。

 

參考資料:

求解兩個字符串的最長公共子序列

動態規划(3)-最長遞增子序列

 

三,代碼實現

錯誤版本1:

 1     private static int lis(int[] arr, int length){
 2         int lis[] = new int[length];
 3         
 4         //init
 5         for(int i = 0; i < length; i++)
 6             lis[i] = 1;
 7         
 8         for(int i = 1; i < length; i++)
 9         {
10             for(int j = 0; j < i; j++)
11             {
12                 
13                 if(arr[i] > arr[j])
14                 {
15                     if(lis[j] + 1 > lis[i])
16                         lis[i] = lis[j] + 1;
17                 }
18                 else{
19                     if(lis[j] > lis[i])
20                         lis[i] = lis[j];
21                 }
22             }
23         }
24         return lis[length - 1];
25     }

第13行if語句會導致bug,arr[i]要大於 j belongs to  0,1,...i-1 中所有的 arr[j]中的最大值,並且 lis[i] 是該最大值 arr[j] 所對應的 lis[j] +1,而不是某個其他的arr[j] 對應的 lis[j]+1

 

正確完整版本:

public class LIS {
    public static int lis(int[] arr){
        if(arr == null || arr.length == 0)
            return 0;
        return lis(arr, arr.length);
    }
    
    private static int lis(int[] arr, int length){
        int lis[] = new int[length];
        
        //init
        for(int i = 0; i < length; i++)
            lis[i] = 1;
        
        for(int i = 1; i < length; i++)
        {
            for(int j = 0; j < i; j++)
            {
//                lis[i]=max{lis[i-1], lis[i-1]+1}
                if(arr[i] > arr[j] && lis[j] + 1 > lis[i])
                    lis[i] = lis[j] + 1;
            }
        }
        
        int max = lis[0];
        for(int i = 1; i < length; i++)
            if(max < lis[i])
                max = lis[i];
        return max;
    }
    
    public static void main(String[] args) {
        int[] arr = {3,1,4,1,5,9,2,6,5};
        int result = lis(arr);
        System.out.println(result);
    }
}


免責聲明!

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



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