1. 程式人生 > >遞迴與動態規劃---最長遞增子序列問題

遞迴與動態規劃---最長遞增子序列問題

【問題】
給定陣列arr,返回arr的最長遞增子序列

【基本思路】

首先介紹時間複雜度為O(N^2)的方法。具體過程如下:

  1. 生成長度為N(arr的長度)的陣列dp,dp[i]表示在以arr[i]結尾的情況下,arr[0…i]中的最長子序列。

  2. dp[0]表示以arr[0]結尾的情況下最長子序列,只有它自己,設為1

  3. 對於dp的其他位置,從左到右依次遍歷,假設遍歷到i,首先在arr[0…i-1]中找到比arr[i]小且相應的dp值最大的位置記為j,arr[j]即為以arr[i]結尾的倒數第二小的數,此時dp[i]的值便可以確定,dp[i] = dp[j] + 1。如果arr[0…i-1]中沒有比arr[i]小的值,則dp[i]直接記為1。

接下來就是利用生成好的dp陣列得到最長的遞增子序列。

首先遍歷找到dp陣列中的最大值maxlen以及下標index,其中maxlen就是最長遞增子序列的長度,arr[index]就是最長遞增子序列的最後一個數字,然後從index向前遍歷陣列arr,找到比arr[index]小的數arr[j]並且dp[j] + 1 = dp[index],這個值就是子序列的倒數第二個數,依次向前遍歷即可得到最長遞增子序列。

以下是使用python3.5實現的程式碼

#最長遞增子序列
def getMaxSubList1(arr):
    def getdp(arr):
        dp = [1
for i in range(len(arr))] for i in range(len(arr)): for j in range(i): if arr[i] > arr[j]: dp[i] = max(dp[i], dp[j]+1) return dp def generateLIS(arr, dp): maxlen = 0 index = 0 for i in range(len(dp)): if
dp[i] > maxlen: maxlen = dp[i] index = i lis = [ for i in range(maxlen)] lis[maxlen-1] = arr[index] maxlen -= 1 for i in range(index, -1, -1): if arr[i] < arr[index] and dp[i]+1 == dp[index]: lis[maxlen-1] = arr[i] maxlen -= 1 index = i return lis if arr == None or len(arr) == 0: return None dp = getdp(arr) return generateLIS(arr, dp)

接下里介紹一個時間複雜度為O(NlogN)的方法。
該方法是在生成dp陣列的時候利用二分查詢來進行優化,通過另外一個輔助陣列ends完成。ends[b]的含義為遍歷到目前位置,長度為b + 1的子序列的最小結尾。過程如下:

  1. 生成長度為N的dp陣列和ends陣列,令dp[0] = 1,ends[0] = arr[0]。
  2. 從左到右遍歷arr的剩餘部分,假設遍歷到位置i,首先在ends中已有的數值中找第一個比arr[i]大的數的位置k(使用二分查詢),把該位置的數替代為arr[i],dp[i] = k + 1。依次遍歷即可得到dp陣列。
def getMaxSubList2(arr):
    def getdp2(arr):
        dp = [0 for i in range(len(arr))]
        ends = [0 for i in range(len(arr))]
        right = 0
        dp[0] = 1
        ends[0] = arr[0]
        for i in range(1, len(arr)):
            l = 0
            r = right
            while l <= r:
                m = (l + r) // 2
                if arr[i] > ends[m]:
                    l = m + 1
                else:
                    r = m - 1
            right = max(right, l)
            dp[i] = l + 1
            ends[l] = arr[i]
        return dp

    def generateLIS(arr, dp):
        maxlen = 0
        index = 0
        for i in range(len(dp)):
            if dp[i] > maxlen:
                maxlen = dp[i]
                index = i
        lis = [0 for i in range(maxlen)]
        lis[maxlen-1] = arr[index]
        maxlen -= 1
        for i in range(index, -1, -1):
            if arr[i] < arr[index] and dp[i]+1 == dp[index]:
                lis[maxlen-1] = arr[i]
                maxlen -= 1
                index = i
        return lis


    if arr == None or len(arr) == 0:
        return None
    dp = getdp2(arr)
    return generateLIS(arr, dp)