Description

給定 n 個數對 pairs,其中 pairs[i] = [left_i, right_i]left_i < right_i。如果 b < c,則可以將 [a, b][c, d] 串成鏈。回傳最長數對鏈的長度。

Example:

Input: pairs = [[1,2],[2,3],[3,4]] Output: 2 (例如 [1,2] -> [3,4])

Intuition#

按右端點排序,貪心選結束最早的數對 — 與「活動選擇問題」相同。

  • 經典的區間調度問題(Activity Selection)
  • 按右端點排序後,貪心選不重疊的數對
  • 每次選右端點最小的,留給後面更多空間

Approaches#

1: DP#

  • 概念: 按左端點排序,dp[i] 表示以第 i 個數對結尾的最長鏈。對每個數對向前找能接上的。
  • 時間複雜度: O(n^2)
  • 空間複雜度: O(n)
class Solution {
    fun findLongestChain(pairs: Array<IntArray>): Int {
        pairs.sortBy { it[0] }
        val n = pairs.size
        val dp = IntArray(n) { 1 }

        for (i in 1 until n) {
            for (j in 0 until i) {
                if (pairs[j][1] < pairs[i][0]) {
                    dp[i] = maxOf(dp[i], dp[j] + 1)
                }
            }
        }
        return dp.max()
    }
}

⭐2: 貪心(按右端點排序)#

  • 概念: 按右端點排序,貪心選擇:若當前數對的左端點大於上一個選的右端點,就選它。
  • 時間複雜度: O(n log n)
  • 空間複雜度: O(1)
class Solution {
    fun findLongestChain(pairs: Array<IntArray>): Int {
        pairs.sortBy { it[1] }
        var count = 1
        var end = pairs[0][1]

        for (i in 1 until pairs.size) {
            if (pairs[i][0] > end) {
                count++
                end = pairs[i][1]
            }
        }
        return count
    }
}

🔑 Takeaways#

  • Pattern: 區間調度(Activity Selection)— 按結束時間排序,貪心選不重疊的
  • 關鍵技巧: 注意是嚴格小於(b < c)而非小於等於;此問題等價於 LIS 但貪心更快