Medium草稿★★★★★O(log(n-k) + k) 時間 · O(1) 空間
Sliding WindowBinary SearchTwo PointersSorting
Patterns🪟 滑動視窗🎯 二分搜尋↔️ 雙指針🔢 排序後處理
尚未複習過

解法已隱藏 — 先讀題目敘述、自己想想看,再點上方按鈕揭曉。

658Find K Closest ElementsSliding WindowMediumSliding Window

給定已排序的整數陣列 arr、整數 k 和整數 x,回傳最接近 xk 個元素,以排序方式回傳。若兩個元素距離相同,較小的優先。

Example:

Input: arr = [1,2,3,4,5], k = 4, x = 3 Output: [1,2,3,4]

Intuition

TIP

核心思路:結果一定是原陣列中連續的 k 個元素。用二分搜尋找到最佳的起始位置。

Approaches

1. Two Pointers (shrink from ends) — O(n) / O(1)
  • Idea: 先取整個陣列,每次比較兩端哪個離 x 更遠,移除較遠的那端,直到剩下 k 個
  • Time: O(n) - 最多移除 n-k 次
  • Space: O(1) - 不計結果
class Solution {
    fun findClosestElements(arr: IntArray, k: Int, x: Int): List<Int> {
        var left = 0
        var right = arr.size - 1
        while (right - left + 1 > k) {
            if (x - arr[left] <= arr[right] - x) {
                right--
            } else {
                left++
            }
        }
        return arr.slice(left..right)
    }
}
⭐ 2. Binary Search for Start Index — O(log(n-k) + k) / O(1)
  • Idea: 二分搜尋找起始索引 left(範圍 [0, n-k])。比較 x - arr[mid]arr[mid+k] - x 決定搜尋方向
  • Time: O(log(n-k) + k) - 二分 + 建立結果
  • Space: O(1) - 不計結果
class Solution {
    fun findClosestElements(arr: IntArray, k: Int, x: Int): List<Int> {
        var left = 0
        var right = arr.size - k

        while (left < right) {
            val mid = left + (right - left) / 2
            // 比較視窗左端點和右端點之外的距離
            if (x - arr[mid] > arr[mid + k] - x) {
                left = mid + 1
            } else {
                right = mid
            }
        }
        return arr.slice(left until left + k)
    }
}

WARNING

比較的是 x - arr[mid]arr[mid+k] - x(注意是 mid+k 不是 mid+k-1),因為我們在比較「是否應該把視窗右移」— 即把 arr[mid] 換成 arr[mid+k] 是否更好。

🔑 Takeaways