LeetCode 解題思路:26. Remove Duplicates From Sorted Array(刪除有序陣列中的重複項)

題目描述 給定一個按照非遞減順序排列的整數陣列 nums,請你原地刪除重複出現的元素,使每個元素只出現一次。元素的相對順序應該保持一致。然後返回 nums 中唯一元素的個數。 假設 nums 的唯一元素個數為 k,為了通過測試,你需要做以下事情: 更改陣列 nums,使 nums 的前 k 個元素包含原來的唯一元素,且順序與原陣列相同 nums 的其餘元素以及 nums 的大小都不重要 什麼是非遞減順序? 在深入解題之前,先理解一個重要概念: 非遞減順序是指陣列中的每個元素都不小於前一個元素。簡單來說,就是「排序好的陣列,但允許有重複值」。 # ✅ 非遞減順序的例子 [1, 2, 3, 4, 5] # 嚴格遞增 [1, 1, 2, 2, 3] # 有重複值(這題的重點!) [5, 5, 5, 5, 5] # 全部相同 [1, 2, 2, 3, 3, 3] # 部分重複 # ❌ 不是非遞減順序 [5, 4, 3, 2, 1] # 遞減 [1, 3, 2, 4] # 有下降視覺化理解 非遞減順序(像爬樓梯,可以有平地): ┌─────┐ ┌─┤ │ ┌─┤ │ │ ─┤ │ │ │ └─┴─┴─────┘ 1 2 2 3 3 關鍵:因為是非遞減順序,相同的元素必定相鄰!範例 範例 1:

LeetCode 解題思路:20. Valid Parentheses(有效的括號)

題目描述 給定一個只包括 '(',')','{','}','[',']' 的字串 s,判斷字串是否有效。 有效字串需滿足: 左括號必須用相同類型的右括號閉合 左括號必須以正確的順序閉合 每個右括號都有一個對應的相同類型的左括號 範例 範例 1: 輸入:s = "()" 輸出:true範例 2: 輸入:s = "()[]{}" 輸出:true範例 3: 輸入:s = "(]" 輸出:false範例 4: 輸入:s = "([)]" 輸出:false 解釋:雖然有對應的括號,但順序不正確範例 5: 輸入:s = "{[]}" 輸出:true限制條件 1 <= s.length <= 10⁴ s 僅由括號 '()[]{}' 組成 核心概念:為什麼要用堆疊? 括號匹配的本質 觀察有效的括號序列,你會發現一個規律:最後出現的左括號,必須最先被匹配。 範例:{[()]} ↓ 遇到 { → 需要找 } 遇到 [ → 需要找 ](但要先處理) 遇到 ( → 需要找 )(但要先處理) 遇到 ) → 匹配最近的 ( 遇到 ] → 匹配最近的 [ 遇到 } → 匹配最近的 {這正是**後進先出(LIFO)**的特性,而堆疊就是為此而生!

演算法基礎:空間複雜度完全指南

什麼是空間複雜度? 空間複雜度(Space Complexity)描述了演算法執行時所需的記憶體空間如何隨著輸入規模增長。它回答了一個關鍵問題:「當資料量變大時,程式需要多少記憶體?」 為什麼空間複雜度很重要? # 情境:處理一個 10GB 的檔案 # 方法 1:全部載入記憶體(可能造成記憶體不足) def process_file_v1(filename): with open(filename, 'r') as f: content = f.read() # 需要 10GB 記憶體! return content.upper() # 方法 2:逐行處理(記憶體使用量固定) def process_file_v2(filename): result = [] with open(filename, 'r') as f: for line in f: # 一次只載入一行 result.append(line.upper()) return ''.join(result) # 方法 3:串流處理(最省記憶體) def process_file_v3(filename, output_filename): with open(filename, 'r') as f_in, open(output_filename, 'w') as f_out: for line in f_in: f_out.write(line.upper())空間複雜度的組成 1. 輸入空間 演算法輸入資料佔用的空間(通常不計入額外空間)。

演算法基礎:時間複雜度完全指南

什麼是時間複雜度? 時間複雜度(Time Complexity)是用來描述演算法執行時間如何隨著輸入規模增長的數學模型。簡單來說,它回答了一個問題:「當資料量變大時,程式會跑多久?」 為什麼需要時間複雜度? 想像你要從一本電話簿中找到某個人的電話號碼: # 方法 1:從頭翻到尾(線性搜尋) def linear_search(phone_book, name): for entry in phone_book: if entry.name == name: return entry.phone return None # 方法 2:利用排序特性(二分搜尋) def binary_search(phone_book, name): left, right = 0, len(phone_book) - 1 while left <= right: mid = (left + right) // 2 if phone_book[mid].name == name: return phone_book[mid].phone elif phone_book[mid].name < name: left = mid + 1 else: right = mid - 1 return None當電話簿有 1000 個號碼時,差異可能不明顯。但如果有 100 萬個號碼呢?這就是為什麼我們需要時間複雜度分析。

LeetCode 21: Merge Two Sorted Lists 詳細解析

題目描述 給定兩個已排序的鏈結串列 list1 和 list2 的頭節點。 將這兩個串列合併成一個已排序的串列。合併後的串列應該透過拼接前兩個串列的節點來建立。 返回合併後鏈結串列的頭節點。 範例 範例 1: 輸入:list1 = [1,2,4], list2 = [1,3,4] 輸出:[1,1,2,3,4,4]範例 2: 輸入:list1 = [], list2 = [] 輸出:[]範例 3: 輸入:list1 = [], list2 = [0] 輸出:[0]限制條件 兩個串列的節點數量範圍為 [0, 50] -100 <= Node.val <= 100 list1 和 list2 都按照非遞減順序排列 解題思路 這道題目要求我們合併兩個已排序的鏈結串列。主要有兩種解法: 方法一:迭代法 使用雙指標的概念,逐一比較兩個串列的當前節點,將較小的節點加入結果串列。 核心概念: 建立一個虛擬頭節點(dummy node)來簡化邊界條件處理 使用一個指標追蹤結果串列的當前位置 比較兩個串列的當前節點,選擇較小的加入結果 處理剩餘的節點 方法二:遞迴法 利用遞迴的特性,每次選擇較小的節點,然後遞迴處理剩餘部分。 核心概念: 基本情況:如果其中一個串列為空,返回另一個串列 比較兩個串列的頭節點 選擇較小的節點,並遞迴處理剩餘部分 程式碼實現 方法一:迭代法 # Definition for singly-linked list. class ListNode: def __init__(self, val=0, next=None): self.

LeetCode 21: Merge Two Sorted Lists 詳細解析

題目描述 給定兩個已排序的鏈結串列 list1 和 list2 的頭節點。 將這兩個串列合併成一個已排序的串列。合併後的串列應該透過拼接前兩個串列的節點來建立。 返回合併後鏈結串列的頭節點。 範例 範例 1: 輸入:list1 = [1,2,4], list2 = [1,3,4] 輸出:[1,1,2,3,4,4]範例 2: 輸入:list1 = [], list2 = [] 輸出:[]範例 3: 輸入:list1 = [], list2 = [0] 輸出:[0]限制條件 兩個串列的節點數量範圍為 [0, 50] -100 <= Node.val <= 100 list1 和 list2 都按照非遞減順序排列 解題思路 這道題目要求我們合併兩個已排序的鏈結串列。主要有兩種解法: 方法一:迭代法 使用雙指標的概念,逐一比較兩個串列的當前節點,將較小的節點加入結果串列。 核心概念: 建立一個虛擬頭節點(dummy node)來簡化邊界條件處理 使用一個指標追蹤結果串列的當前位置 比較兩個串列的當前節點,選擇較小的加入結果 處理剩餘的節點 方法二:遞迴法 利用遞迴的特性,每次選擇較小的節點,然後遞迴處理剩餘部分。 核心概念: 基本情況:如果其中一個串列為空,返回另一個串列 比較兩個串列的頭節點 選擇較小的節點,並遞迴處理剩餘部分 程式碼實現 方法一:迭代法 # Definition for singly-linked list. class ListNode: def __init__(self, val=0, next=None): self.
0%