基本資料#
難易度: easy 第一次嘗試: 2025-04-14
- 總花費時間:2:00.00
解題思路#
我們很容易會想到用暴力解法來處理這題。
由於 nums 的範圍相對較小,一個 O(n^3) 的解法是可以接受的,不會導致 TLE(Time Limit Exceeded)。
從 Leetcode 討論區看到一個很棒的提示:我們其實不需要三重迴圈遍歷陣列。只要遍歷 j 和 k,並透過條件去反推出可能的 i 值的範圍,搭配前綴和(prefix sum)來加速計算,就能有效求解。這個方法的精髓有兩個:
20250419 更新#
1. 找出合法的 arr[i] 範圍#
根據題目的條件:
- |arr[i] - arr[j]| <= a
- |arr[i] - arr[k]| <= c
我們可以反推出 arr[i] 合法值的上下界:
- 最小值為以下三者的最大值: - arr[j] - a
- arr[k] - c
- 0
 
- 最大值為以下三者的最小值: - arr[j] + a
- arr[k] + c
- max(arr)
 
這樣我們就得到了 arr[i] 合法的範圍 [left, right]。
2. Prefix Sum 的建立方式#
與傳統的 prefix sum 從一開始就建立整份陣列不同,這個方法是在每次遍歷到索引 j 時才動態更新 prefix sum。
為什麼這樣設計?因為題目要求 i < j,所以我們只需要統計在 j 之前出現過的 arr[i] 值。因此,在處理 (j, k) 組合時,我們可以直接查 prefix sum 裡有多少值是落在合法的 arr[i] 區間內。
舉例來說,若 arr = [3, 0, 1, 1, 9, 7],當 j = 3, k = 0 時,prefix sum 尚未更新,因此仍是全為 0,因為還沒有任何值加入過。等到處理完所有 k 之後,我們會把 arr[j] = 3 加入 prefix sum 中,更新如下:
ps = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
接下來在後續的 (j, k) 遍歷中,只要計算出 [left, right],就能利用這個 prefix sum 快速查出有多少個 i 值是符合條件的。例如某個區間剛好包含數字 3,那就代表 3 是合法的 arr[i] 候選值。
這樣的做法能夠有效地將原本暴力解法的時間複雜度從 O(n^3) 降低為 O(n^2),大大提升效率。
解法#
class Solution:
    def countGoodTriplets(self, arr: List[int], a: int, b: int, c: int) -> int:
        res = 0
        n = len(arr)
        for i in range(n):
            for j in range(i+1, n):
                for k in range(j+1, n):
                    if abs(arr[i]-arr[j]) <= a and abs(arr[j]-arr[k]) <= b and abs(arr[i]-arr[k]) <= c:
                        res +=1
        return res
O(n^2)#
class Solution:
    def countGoodTriplets(self, arr: List[int], a: int, b: int, c: int) -> int:
        
        # iter j,k and use prefix sum to count i
        limit = max(arr)+1
        n = len(arr)
        ps = [0]*limit
        res = 0
        for j in range(n):
            for k in range(j+1, n):
                if abs(arr[j]-arr[k]) <= b:
                    left = max(arr[j] - a, arr[k] - c, 0)
                    right = min(arr[j] + a, arr[k] + c, limit-1)
                    if left <= right:
                        res += ps[right] - (0 if left == 0 else ps[left-1])
            for idx in range(arr[j], limit):
                ps[idx] += 1
        return res
收穫#
這個技巧的關鍵在於動態更新 prefix sum,在遍歷元素的同時即時取得符合條件的數量。
遇到的問題#
NA

