繼第 3 天的「121. Best Time to Buy and Sell Stock」,今天來解 162 這題!還沒看過第 3 天或再之前天數的朋友,歡迎也去看看~
我們開始吧!
這題有在 MIT 6.006 演算法概論被提出來,有興趣了解詳細的朋友歡迎到文末參考相關影片和講義 :)
給予一個 0-indexed 整數 nums 陣列,請回傳最高值元素 (peak element) ,如果有多個最高值,請回傳任一個最高值的索引。
最高值元素: 大於其臨近元素。
You must write an algorithm that runs in O(log n) time.
題目中要求以 O(log n)
的時間複雜度完成。
不過作為比較,這篇會先以線性走訪解題,再來達成這個需求。
nums = [1, 2, 3, 1]
peak 元素為 3 ,因此需回傳他的索引值:
2
核心邏輯
走訪
class Solution {
func findPeakElement(_ nums: [Int]) -> Int {
for index in 0..<(nums.count - 1) {
if nums[index] > nums[index + 1] {
return index
}
}
return nums.count - 1
}
}
Big O | 說明 | |
---|---|---|
時間複雜度 | O(n) | 線性走訪 |
空間複雜度 | O(1) | 走訪之間沒有使用到多餘的記憶體 |
題目提到需要以 O(log n)
的時間複雜度解題,因此就可以用二元搜尋來解題。因為二元搜尋是能夠將 O(n)
降為 O(log n)
的主要工具之一。
因此
只要看到題目提到
O(log n)
就要想到可能可以用二元搜尋來解題
二元搜尋是透過中間點比較中間點、左半邊和右半邊的演算法,優點就是經過一次的比較舊能夠去掉一半不需要比較,從而大大改善運行時間。
因此這題只要以二元搜尋的演算法為原型來變化即可,因此我們要定下三個主要的邏輯
條件和處理方式如下
條件 | 處理方式 | |
---|---|---|
1 | middle 大於右 1 位元素 | 把 right flag 設定為 middle ,接著針對 左半邊 搜尋 |
2 | 剩餘的條件,即為: middle 小於右 1 位元素 | 把 left flag 設定為 middle + 1 ,接著針對 右半邊 搜尋 |
class Solution {
func findPeakElement(_ nums: [Int]) -> Int {
var left = 0
var right = nums.count - 1
while left < right {
let middle = (left + right) / 2
if nums[middle] > nums[middle + 1] {
right = middle
} else {
left = middle + 1
}
}
return left
}
}
Big O | 說明 | |
---|---|---|
時間複雜度 | O(log n) | 線性走訪 |
空間複雜度 | O(1) | 走訪之間只有用到常數個變數 → O(2),因此可寫作 O(1) |
解法 | 時間複雜度 | Runtime | 空間複雜度 | Memory |
---|---|---|---|---|
1. 線性走訪 | O(n) | 21 ms | O(1) | 14.16 MB |
2. 二元搜尋 | O(log n) | 14 ms | O(1) | 13.66 MB |
可能是測試案例的關係,線性走訪和二元搜尋兩者的 runtime 會很接近,有時候甚至線性走訪會比較快。不過就時間複雜度來說,二元搜尋還是佔優勢。
如果有任何回饋或是文中寫不清楚的地方,歡迎在下面留言!
今天就到這裡,明天見!