Description

給定一個整數陣列 nums,找到任意一個峰值元素並回傳其索引。峰值元素是嚴格大於其相鄰元素的元素。假設 nums[-1] = nums[n] = -∞。要求 O(log n) 時間複雜度。

Example:

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

Intuition#

比較 mid 與 mid+1,往較大的方向走一定能找到峰值(因為邊界是 -∞)。

  • 因為陣列兩端視為 -∞,只要不在邊界就一定存在峰值
  • 如果 nums[mid] < nums[mid+1],右邊一定有峰值
  • 如果 nums[mid] > nums[mid+1],左邊(含 mid)一定有峰值

Approaches#

1: Linear Scan#

  • 概念: 找第一個 nums[i] > nums[i+1] 的位置
  • 時間複雜度: O(n)
  • 空間複雜度: O(1)
Linear Scan 程式碼
class Solution {
    fun findPeakElement(nums: IntArray): Int {
        for (i in 0 until nums.size - 1) {
            if (nums[i] > nums[i + 1]) return i
        }
        return nums.size - 1
    }
}
  • 概念: 比較 nums[mid]nums[mid+1],往較大的方向收縮
  • 時間複雜度: O(log n)
  • 空間複雜度: O(1)
class Solution {
    fun findPeakElement(nums: IntArray): Int {
        var left = 0
        var right = nums.size - 1
        while (left < right) {
            val mid = left + (right - left) / 2
            if (nums[mid] < nums[mid + 1]) {
                left = mid + 1
            } else {
                right = mid
            }
        }
        return left
    }
}

這題不需要陣列有序也能二分!關鍵觀察是邊界為 -∞,所以只要往上坡方向走就一定能找到山頂。

🔑 Takeaways#

  • Pattern: 非傳統二分搜尋(不需要有序陣列)
  • 關鍵技巧: 二分搜尋不只能用在排序陣列上,只要能找到一個判斷條件將搜尋空間分成兩半即可。這題的判斷條件是「上坡 vs 下坡」