300.Longest-Increasing-Subsequence

最長遞增子序列(Longest Increasing Subsequence, LIS)

題目

Given an integer array nums, return the length of the longest strictly increasing subsequence.

A subsequence is a sequence that can be derived from an array by deleting some or no elements without changing the order of the remaining elements. For example, [3,6,2,7] is a subsequence of the array [0,3,1,6,2,2,7].

Example 1:

Input: nums = [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.

Example 2:

Input: nums = [0,1,0,3,2,3]
Output: 4

Example 3:

Input: nums = [7,7,7,7,7,7,7]
Output: 1

Constraints:

  • 1 <= nums.length <= 2500
  • -104 <= nums[i] <= 104

Follow up: Can you come up with an algorithm that runs in O(n log(n)) time complexity?

題目大意

給你一個整事的數組 nums, 找到其中最長遞增子序列的長度

  • 子序列不一定是連續的
  • 子串一定是連續的

解題思路

方法一: DP

dp[i]表示nums[i]這個數結尾的最長遞增子序列的長度 譬如要找dp[5]的直, 也就是球nums[5]為結尾的最長遞增子序列 只要找到結尾比nums[5]小的子序列, 然後把 nums[5]接到最後, 就可以形成新的遞增子序列, 而這個新子序列長度+1

方法二: DP + 二分搜尋

patience sorting (耐心排序) 給一組 [6, 3 ,5 ,10, 11, 2, 9, 14, 13, 7, 4, 8, 12] 只能把數字小的放在數字大的堆上, 如果當前數字太大沒有可以放置的堆, 則新建一個堆, 如果當前排有很多堆可以選擇, 則選擇最左邊的那一堆放, 因為這樣可以確保堆頂是有序的(2,4,7,8,12) 6 5 10 11 14 3 4 9 8 13 2 7 12

堆數就是最長遞增子序列的長度 [3,5,7,8,12]

來源

解答

https://github.com/kimi0230/LeetcodeGolang/blob/master/Leetcode/0300.Longest-Increasing-Subsequence/main.go

package longestincreasingsubsequence

import "fmt"

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

// DP 解法 O(n^2)
func LengthOfLIS(nums []int) int {
    dp := make([]int, len(nums))
    for idx := 0; idx < len(dp); idx++ {
        dp[idx] = 1
    }

    res := 0
    for i := 0; i < len(nums); i++ {
        for j := 0; j < i; j++ {
            if nums[i] > nums[j] {
                // 找到下一個數值比現在的大
                dp[i] = max(dp[i], dp[j]+1)
            }
        }
        res = max(res, dp[i])
    }
    // fmt.Println(dp)
    return res
}

func LengthOfLIS2(nums []int) int {
    dp := make([]int, len(nums)+1)
    dp[0] = 0
    res := 0

    for i := 1; i <= len(nums);="" i++="" {="" for="" j="" :="1;" <="" i;="" j++="" if="" nums[i-1]=""> nums[j-1] {
                // 找到前面比現在結尾還小的子序列
                dp[i] = max(dp[i], dp[j])
            }
        }
        dp[i] = dp[i] + 1
        res = max(res, dp[i])
    }
    // fmt.Println(dp)
    return res
}

// DP + 二分搜尋:patience sorting. O(nlogn)
func LengthOfLISPatience(nums []int) int {
    top := make([]int, len(nums))

    // 牌堆數初始化為0
    piles := 0

    for i := 0; i < len(nums); i++ {
        poker := nums[i]

        // 搜尋左側邊界的二元搜尋
        left, right := 0, piles
        // fmt.Printf("i:%d\tL:%d\tR:%d\n", i, left, right)
        for left < right {
            mid := left + (right-left)/2
            if top[mid] > poker {
                // 現在的牌比堆小, 縮小範圍
                right = mid
            } else if top[mid] < poker {
                // 現在的牌比堆大
                left = mid + 1
            } else {
                right = mid
            }
        }

        // 沒有找到堆, 建立一個新的堆
        if left == piles {
            piles++
        }
        // 再把這張牌放在堆的頂端
        top[left] = poker
    }
    fmt.Println(top)
    return piles
}
go test -benchmem -run=none LeetcodeGolang/Leetcode/300.Longest-Increasing-Subsequence -bench=.
goos: darwin
goarch: amd64
pkg: LeetcodeGolang/Leetcode/300.Longest-Increasing-Subsequence
cpu: Intel(R) Core(TM) i5-8259U CPU @ 2.30GHz
BenchmarkLengthOfLIS-8                  18300901                64.77 ns/op           64 B/op          1 allocs/op
BenchmarkLengthOfLIS2-8                 17410562                68.98 ns/op           80 B/op          1 allocs/op
BenchmarkLengthOfLISPatience-8          20768851                58.47 ns/op           64 B/op          1 allocs/op
PASS
ok      LeetcodeGolang/Leetcode/300.Longest-Increasing-Subsequence      3.812s
© Kimi Tsai all right reserved.            Updated : 2024-05-06 09:36:37

results matching ""

    No results matching ""