跳至主要内容

LeetCode 演算法解題筆記

1. Palindrome Number (回文數字)

題目描述

判斷一個整數是否為回文數字(正讀反讀都相同)

解題思路

  • 方法1: 轉成字串後反轉比較
  • 方法2: 數學方法反轉數字

JavaScript 實作

我的寫法(轉回數字比較)

function isPalindrome(x) {
if(x < 0)
return false;

let reversed = String(x).split('').reverse().join('');
reversed = Number(reversed);

return x === reversed;
}

方法1: 字串反轉

function isPalindrome(x) {
if (x < 0) return false;
const str = x.toString();
return str === str.split('').reverse().join('');
}

方法2: 數學反轉(更省空間)

function isPalindrome(x) {
if (x < 0 || (x % 10 === 0 && x !== 0)) return false;

let reversed = 0;
let original = x;

while (original > reversed) {
reversed = reversed * 10 + original % 10;
original = Math.floor(original / 10);
}

return original === reversed || original === Math.floor(reversed / 10);
}

時間複雜度

  • 時間: O(log n)
  • 空間: O(1) (數學方法)

2. Two Sum (HashMap)

題目描述

在陣列中找出兩個數字,使其和等於目標值,返回索引

解題思路

  1. 暴力法: 兩層迴圈檢查所有配對組合
  2. HashMap 法: 使用 HashMap 儲存已遍歷的數字及其索引,一次遍歷即可找到答案

JavaScript 實作

方法1: 暴力法(兩層迴圈)

function twoSum(nums, target) {
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) { // 從 i+1 開始
if (nums[i] + nums[j] === target) {
return [i, j];
}
}
}

return [];
}
  • i = 目前位置
  • j = 目前位置 + 1

方法2: HashMap 法

function twoSum(nums, target) {
let obj = {}; // { 數字: 索引}

for(let i = 0; i < nums.length; i++) {
let currentNum = nums[i];
let complement = target - currentNum;

if(complement in obj) {
return [obj[complement], i];
}

obj[currentNum] = i;
}

return [];
}

方法3: HashMap 法(使用 Map)

function twoSum(nums, target) {
const map = new Map();

for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];

if (map.has(complement)) {
return [map.get(complement), i];
}

map.set(nums[i], i);
}

return [];
}

方法比較

方法時間複雜度空間複雜度優缺點
暴力法O(n²)O(1)❌ 效率低,但容易理解
HashMapO(n)O(n)✅ 最優解,效能最佳

HashMap 核心概念

  • 目標: 避免重複遍歷,將「尋找配對」變成 O(1) 查找
  • 技巧: 儲存 數字 → 索引 的映射關係
  • 邏輯: 當遍歷到 nums[i] 時,檢查 target - nums[i] 是否已經出現過

3. Valid Parentheses (Stack)

題目描述

判斷字串中的括號是否有效配對(包含 (), [], {}

解題思路

使用 Stack 結構,遇到左括號推入,遇到右括號檢查是否匹配

JavaScript 實作

我的寫法

function isValid () {
let stack = [];
let pairs = {
')': '(',
'}': '{',
']': '['
}

for(let i = 0; i < s.length; i++) {
let char = s[i];

if(char === '(' || char === '{' || char === '[') {
stack.push(char);
} else if (char === ')' || char === '}' || char === ']') {
if(stack.length === 0) {
return false;
}

let top = stack.pop();
if(pairs[char] !== top) {
return false;
}
}
}

return stack.length === 0;
}

方法一

function isValid(s) {
const stack = [];
const pairs = {
')': '(',
']': '[',
'}': '{'
};

for (let char of s) {
if (char === '(' || char === '[' || char === '{') {
stack.push(char);
} else if (char === ')' || char === ']' || char === '}') {
if (stack.length === 0 || stack.pop() !== pairs[char]) {
return false;
}
}
}

return stack.length === 0;
}

時間複雜度

  • 時間: O(n)
  • 空間: O(n)

4. Remove Element (Two Pointers)

題目描述

移除陣列中所有等於指定值的元素,返回新長度。 有效元素後的系統不會檢查。

解題思路

使用雙指針技巧,慢指針記錄有效位置,快ㄏ指針遍歷陣列

JavaScript 實作

function removeElement(nums, val) {
let slow = 0;

for (let fast = 0; fast < nums.length; fast++) {
if (nums[fast] !== val) {
nums[slow] = nums[fast];
slow++;
}
}

return slow;
}
  • slow 代表的是「下一個要放置的位置」,故回傳不用 +1

時間複雜度

  • 時間: O(n)
  • 空間: O(1)

題目描述

在排序陣列中找到目標值的插入位置

解題思路

使用二分搜尋法,在 O(log n) 時間內找到正確位置

JavaScript 實作

function searchInsert(nums, target) {
let left = 0;
let right = nums.length - 1;

while (left <= right) {
const mid = Math.floor((left + right) / 2);

if (nums[mid] === target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}

return left;
}

時間複雜度

  • 時間: O(log n)
  • 空間: O(1)

6. Best Time to Buy and Sell Stock (Min Tracking)

題目描述

找出買賣股票的最大利潤(只能交易一次)

解題思路

追蹤最低買入價格,計算當前價格賣出的最大利潤

JavaScript 實作

我的寫法

function maxProfit(prices) {
let minPrice = prices[0];
let maxProfit = 0;

for(let i = 1; i < prices.length; i++) {
if(minPrice > prices[i]) {
minPrice = prices[i];
} else {
maxProfit = Math.max(maxProfit, prices[i] - minPrice);
}
}

return maxProfit;
}

方法: Min Tracking

function maxProfit(prices) {
let minPrice = prices[0];
let maxProfit = 0;

for(let i = 1; i < prices.length; i++) {
// 每天都計算如果今天賣出的利潤
maxProfit = Math.max(maxProfit, prices[i] - minPrice);

// 每天都檢查是否有新的最低價格
minPrice = Math.min(minPrice, prices[i]);
}

return maxProfit;
}

時間複雜度

  • 時間: O(n)
  • 空間: O(1)

7. Longest Substring Without Repeating Characters (Sliding Window)

題目描述

找出字串中最長的無重複字元子字串長度

解題思路

使用滑動視窗技巧,維護一個無重複字元的視窗

JavaScript 實作

方法1: 使用 Set

function lengthOfLongestSubstring(s) {
const charSet = new Set();
let left = 0;
let maxLength = 0;

for (let right = 0; right < s.length; right++) {
while (charSet.has(s[right])) {
charSet.delete(s[left]);
left++;
}

charSet.add(s[right]);
maxLength = Math.max(maxLength, right - left + 1);
}

return maxLength;
}

方法2: 使用 Object

function lengthOfLongestSubstring(s) {
const charObj = {};
let left = 0;
let maxLength = 0;

for (let right = 0; right < s.length; right++) {
while (s[right] in charObj) {
delete charObj[s[left]];
left++;
}

charObj[s[right]] = true; // 或者存入索引位置
maxLength = Math.max(maxLength, right - left + 1);
}

return maxLength;
}

時間複雜度

  • 時間: O(n)
  • 空間: O(min(m, n)),m 是字元集大小

演算法技巧總結

技巧適用情境時間複雜度空間複雜度
HashMap快速查找、配對問題O(1) 查找O(n)
Stack括號匹配、後進先出O(1) 操作O(n)
Two Pointers排序陣列、原地操作O(n)O(1)
Binary Search排序陣列搜尋O(log n)O(1)
Min Tracking追蹤最值、動態規劃O(n)O(1)
Sliding Window子字串、子陣列問題O(n)O(k)

解題心得

  1. 選擇合適的資料結構:HashMap 用於快速查找,Stack 用於配對問題
  2. 善用雙指針:可以減少空間複雜度,適合排序陣列
  3. 二分搜尋:排序陣列的利器,能將 O(n) 降到 O(log n)
  4. 滑動視窗:處理連續子序列問題的高效方法
  5. 貪心思維:如股票問題,局部最優解導向全域最優解