Skip to content
本站總訪問量
本站訪客數 人次

LIS 最長遞增子序列

介紹

用下方的數列舉例

[10, 22, 33, 50, 60, 80]

解釋:

  1. 10 是第一個數字,作為起始點。
  2. 2210 大,加入序列。
  3. 922 小,跳過。
  4. 3322 大,加入序列。
  5. 2133 小,跳過。
  6. 5033 大,加入序列。
  7. 4150 小,跳過。
  8. 6050 大,加入序列。
  9. 8060 大,加入序列。

最終序列為 10, 22, 33, 50, 60, 80 為什麼是這幾個數字? 這些數字是從原數列中選出的,並且滿足以下條件:

  1. 遞增:每個數字都比前一個數字大。
  2. 最長:沒有其他遞增子序列的長度比這更長。

1. 動態規劃方法

這是LIS問題的標準解決方法。通常使用一個數組來存儲以每個元素結尾的最長遞增子序列的長度。

python
def LIS_dp(nums):
    n = len(nums)
    dp = [1] * n
    for i in range(1, n):
        for j in range(i):
            if nums[i] > nums[j]:
                dp[i] = max(dp[i], dp[j] + 1)
    return max(dp)

效率版

python
def LIS_effiently(nums):
    dp = []
    for num in nums:
        i = 0
        j = len(dp)
        while i != j:
            mid = (i + j) // 2
            if dp[mid] < num:
                i = mid + 1
            else:
                j = mid

        if i == len(dp):
            dp.append(num)
        else:
            dp[i] = num

    return len(dp)

2. (推)二分查找方法

這種方法使用二分查找來優化動態規劃解法,減少查找元素的時間覆雜度。

python
def LIS_with_bisect(nums):
    from bisect import bisect_left
    dp = []
    for num in nums:
        i = bisect_left(dp, num)
        if i == len(dp):
            dp.append(num)
        else:
            dp[i] = num
    return len(dp)

3. 貪心算法方法

使用貪心算法可以找到LIS的一種逼近方法。它的基本思想是盡量選擇比前一個元素大的元素,構建遞增子序列。

python
def LIS_greedy(arr):
    n = len(arr)
    lis = [arr[0]]

    for i in range(1, n):
        if arr[i] > lis[-1]:
            lis.append(arr[i])

    return len(lis)

4. 分治法方法

分治法也可以用來解決LIS問題,將問題分解為更小的子問題。

python
def LIS_divide_conquer(arr):
    def _LIS(arr, n):
        if n == 1:
            return 1
        max_ending_here = 1
        for i in range(1, n):
            subproblem = _LIS(arr, i)
            if arr[i - 1] < arr[n - 1] and subproblem + 1 > max_ending_here:
                max_ending_here = subproblem + 1
        return max(max_ending_here, _LIS(arr, n - 1))

    n = len(arr)
    return _LIS(arr, n)
python
arr = [10, 22, 9, 33, 21, 50, 41, 60, 80]
print("LIS_dp:", LIS_dp(arr))
print("LIS_with_bisect:", LIS_with_bisect(arr))
print("LIS_greedy:", LIS_greedy(arr))
print("LIS_divide_conquer:", LIS_divide_conquer(arr))

Contributors

The avatar of contributor named as lucashsu95 lucashsu95

Changelog