主題: 學習筆記

LeetCode 1732:TypeScript 單次掃描找最高海拔

用目前海拔與最高海拔兩個狀態,完成 O(n) 時間、O(1) 額外空間的單次掃描。

這題不需要記住每一站的海拔。維護兩個數值就夠了:目前海拔與目前看過的最高海拔。每經過一段路,先更新目前海拔,再更新最高海拔。

題目

對應原題:LeetCode 1732:Find the Highest Altitude

自行車從高度 0 出發。gain[i] 是第 i 段路程帶來的高度變化。回傳旅途中曾到達的最高高度,起點也算。

例如,[-5, 1, 5, 0, -7] 對應的高度依序是 [0, -5, -4, 1, 1, -6],答案為 1

不必建立所有海拔

可以先建立一個陣列,放入每一站的海拔,再從中找最大值。不過每走一段路,只會多出一個新海拔,而我們只關心它是否超過目前最高值。

保留目前海拔與最高海拔,就能在讀取每個 gain 後立刻完成判斷。

function largestAltitude(gain: number[]): number {
  let currentAltitude = 0;
  let maxAltitude = currentAltitude;

  for (let index = 0; index < gain.length; index += 1) {
    currentAltitude += gain[index];
    maxAltitude = Math.max(maxAltitude, currentAltitude);
  }

  return maxAltitude;
}

為什麼正確

每次迴圈開始時,currentAltitude 是目前所在位置的海拔,maxAltitude 是起點到目前位置之間的最高海拔。

加上 gain[index] 後,currentAltitude 變成下一個位置的海拔。接著用 Math.max() 比較新海拔與舊的最高海拔,因此 maxAltitude 也涵蓋下一個位置。迴圈結束時,它看過起點與所有路段終點,回傳值就是全程最高海拔。

起點的高度為 0,所以一開始就要把 maxAltitude 設為 0。若所有路段都往下,答案仍然是 0

最小驗證

import assert from "node:assert/strict";

assert.strictEqual(largestAltitude([-5, 1, 5, 0, -7]), 1);
assert.strictEqual(largestAltitude([-4, -3, -2, -1, 4, 3, 2]), 0);
assert.strictEqual(largestAltitude([3, -2, 4, -10]), 5);

第二個案例會檢查是否把起點納入答案。第三個案例則確認最高點不一定在最後一站。

複雜度

陣列只掃描一次,時間是 O(n)。函式只使用 currentAltitudemaxAltitude 兩個數值,額外空間是 O(1)

這兩個值是會更新的變數,不是常數;空間複雜度看的是需要多少個儲存位置,而不是變數是否改變。

常見陷阱

  • 忘記起點高度 0,全程下降時回傳負數。
  • 先比較最高海拔,後更新目前海拔,漏掉剛抵達的新位置。
  • 回傳最後海拔,而不是全程最高海拔。
  • 建立完整海拔陣列,卻沒有需要使用其中的中間值。

可遷移的觀念

這是串流掃描的常見分工。currentAltitude 記錄目前狀態,maxAltitude 記錄到目前為止的最佳結果。只要資料可依序讀取,通常不必保存所有歷史資料。


外部參考連結