深入淺出談 Queue:順序很重要

photo by John Cameron 剛好昨天看了小Lin老師的影片,提到委內瑞拉這個國家,天天都在排隊 加油排隊、去超市也排隊 排隊這件事,其實跟系統處理工作的順序一樣 順序一亂,待季就大條了,這時候使用Queue來管理執行順序,是一個很好的方法 什麼是 Queue Queue 是一種 先進先出 (First-In,First-Out,FIFO) 的資料結構 Queue跟Stack一樣,定義了使用者只能用那些操作 換來一致性的時間複雜度 Queue的時間複雜度 操作 Queue 時間複雜度 push 插入資料到最後位置 O(1) pop 刪除最前面的資料 O(1) front 取得最前面的資料 O(1) 使用Queue最佳場景 假設現在有三個Job依序執行,而執行中很可能因為系統當機導致需要重做 這時候應該把Job放在哪種資料結構中: A:List搭配index B:Queue 看起來A、B都可以,實際上會造成很大的落差 情境A List搭配index list = [A, B, C] i = load_index() // 持久化的進度 while i < list.length: job = list[i] do_side_effect(job) // 例如:寄信 / 扣款 / 發送檔案 // 這裡可能成功、也可能一半成功、也可能成功但你還沒來得及記錄 i = i + 1 save_index(i) // 持久化進度 crash可能發生在: ...

February 5, 2026 · 1 min · Yen Tsai

深入淺出談 Hash Table:從理論 O(1) 到實作中的 rehash 現實

photo by Jessica Johnston 什麼是 Hash Table Hash Table 是一種透過 hash function 將 key 映射到 bucket, 以達成 平均 O(1) 查找效率 的資料結構 資料通常以 key–value 的形式儲存, 使用者只需要提供 key,就能快速取得對應的 value 在實作上,Hash Table 內部會維護一組 bucket array, 每個 bucket 可以儲存一個或多個元素 當資料量持續增加、bucket 內元素變得過於擁擠時, Hash Table 會透過 resize / rehash 重新分配資料, 以維持查找效能 為什麼 Hash Table 可以快速查詢資料? Hash Table 能快速查詢資料的關鍵,在於 hash function 當一個 key 被插入或查詢時, Hash Table 會先對 key 計算 hash 值, 再將 hash 值映射成一個 bucket index, 用來決定資料應該存放或查找的位置 ...

January 28, 2026 · 3 min · Yen Tsai

深入淺出談 Stack:為什麼「後進先出」不是限制,而是力量

photo by Reed Mok 玩過河內塔嗎? 這是是一個只能從一個桿子中拿最上面圓盤的遊戲, 然後再把圓盤放到另一個桿子的最上方 看到Stack的結構馬上就想到這個例子 什麼是 Stack 一個定義好行為的資料結構 使用者只能把資料一層一層疊在最上方 使用者一次只能取最上方的資料 為什麼要定義行為 先看看Array跟Stack的差異 操作 Array Stack Search 可以查詢指定index 只能看最上方資料 Insert 可以Insert指定index 只能push資料到最上方 Remove 可以Remove指定index 只能pop最上方資料 可以看到Array跟Stack的差異在於限制了行為:新增、刪除、查詢的位置, 為的是換來操作的一致性, 跟較好的時間複雜度 Stack 的時間複雜度 操作 複雜度 說明 讀取 (Seek) O(1) 讀取最上方資料 插入 (Push) O(1) 插入資料到最上方 刪除 (Pop) O(1) 刪除最上方資料 使用 Stack 的最佳場景 後進先出的場景 剪貼簿的Undo, Redo功能 Rollback 實作Undo, Redo功能 #include <iostream> #include <stack> #include <string> using namespace std; int main() { stack<string> undoStack; stack<string> redoStack; while (true) { cout << "\nChoose:\n" << "1) Add data\n" << "2) Undo\n" << "3) Redo\n" << "4) Exit\n" << ">> "; int choice = 0; if (!(cin >> choice)) break; if (choice == 1) { cout << "Enter data (one word): "; string x; cin >> x; undoStack.push(x); while (!redoStack.empty()) redoStack.pop(); // 清空 redo cout << "Added: " << x << endl; } else if (choice == 2) { if (undoStack.empty()) { cout << "Nothing to undo." << endl; } else { string x = undoStack.top(); undoStack.pop(); redoStack.push(x); cout << "Undo: " << x << endl; } } else if (choice == 3) { if (redoStack.empty()) { cout << "Nothing to redo." << endl; } else { string x = redoStack.top(); redoStack.pop(); undoStack.push(x); cout << "Redo: " << x << endl; } } else if (choice == 4) { cout << "Bye." << endl; break; } else { cout << "Invalid choice." << endl; } cout << "Undo size: " << undoStack.size() << ", Redo size: " << redoStack.size() << endl; if (!undoStack.empty()) cout << "Undo top: " << undoStack.top() << endl; if (!redoStack.empty()) cout << "Redo top: " << redoStack.top() << endl; } return 0; } 程式碼說明: ...

January 27, 2026 · 2 min · Yen Tsai

深入淺出談 Linked List:為什麼它只能一個一個走

photo by Shubham Dhage 你玩過心理測驗嗎? 有些心理測驗的題目是這樣的 你是男生, 請跳到第 8 題, 妳是女生請跳到第 3 題… 這樣照著前一個指令跳到下一個指定位置的遊戲真的很像 Linked List 的結構 什麼是 Linked List 一個資料結構, 每一筆資料除了自己的值外, 還知道下一筆資料是誰, 因此所有操作都必須照順序進行 Linked List 的時間複雜度 操作 複雜度 說明 讀取 (Read) O(n) 必須從頭開始遍歷資料 插入 (Insert) O(1) 在已知插入的位置時, 只需調整前後關係 刪除 (Delete) O(1) 在已知刪除的位置時, 只需修改前後關係 使用 Linked List 的最佳場景 已知要在某個元素的位置, 並且在元素前後頻繁操作插入刪除 這也是 LRU Cache 常使用 Linked List 的原因之一

January 26, 2026 · 1 min · Yen Tsai

深入淺出談 Array:從記憶體配置看時間複雜度

photo by Reed Mok 想像你是一位老師,要管理一個新班級的學生名單 人數少的時候,你可以直接用名字來記住每一位學生,但當學生人數一多,要快速找到某一位學生就變得困難 情境 A:變數分散管理 如果我們像這樣單獨宣告變數來存學生名字: string student1 = "Alice"; string student2 = "Bob"; string student3 = "Charlie"; // ... 如果有 50 個學生,變數會多到無法管理 這種方式的問題在於: 沒有順序性:變數之間沒有邏輯上的關聯 無法索引:不能用「第 3 個學生」這種方式來存取 難以自動化:無法用迴圈(Loop)來批次處理這群資料 情境 B:使用 Array 統一管理 這時候,如果替每位學生分配一個固定的「座號」,並依照座號的順序將學生資料排排站(存放起來),只要知道座號,就能直接找到對應的學生 struct Student { string name; }; // 學生名單 (Array) Student students[] = { {"Alice"}, // index 0 (座號 1) {"Bob"}, // index 1 (座號 2) {"Charlie"} // index 2 (座號 3) }; const int numStudents = 3; Student* findStudentBySeatNumber(int seatNumber) { // 檢查座號是否合法 if (seatNumber < 1 || seatNumber > numStudents) { return nullptr; } // 透過索引直接取得資料 (Array Index 從 0 開始) return &students[seatNumber - 1]; } 這就是 Array 的核心精神 ...

January 23, 2026 · 2 min · Yen Tsai

Template Method Pattern

在專案中有個需求要產生 100+ 份報表,格式包含Word與PDF,這些報表有幾個共同特性: 都需要從 Word 樣板產生 樣板中使用 unique token(例如 [XXX])作為預填欄位 產出前需要先組合資料、預填內容 有些最後還要轉成 PDF 很直覺的作法是:每新增一個報表, 就寫一份產生報表邏輯 短期內可以快速交付功能, 但是造成的問題是: 每產生一個報表就會新增一套九成像的程式碼邏輯並散落在專案各處 想改一個共通行為要改很多地方 接手維護的工程師不知道哪個是標準寫法 真正的問題不是報表數量變多,而是同一個產生流程,被複製成太多版本 Template Method Pattern 首先要釐清的兩件事情是: 哪些事情在報表中一定會做 哪些事情每份報表都不一樣 不會變的部分 讀取報表樣板 報表資料預填 輸出檔案 是否轉換成PDF檔案 會變的部分 報表的資料來源 預填欄位的內容與資料格式 輸出的檔案名稱 運用Template Method Patten的核心價值就在於: 把一定會做的流程固定下來 只把會變的部分交給子類別實作 避免每個報表各自定義一套自己的產生流程 實作 Template Method Patten 把報表產生流程抽象化,其實每份報表都在做同一件事: 取得產生報表所需的資料 讀取樣板文件, 填入預填資料 輸出文件 在沒有Template Method Patten情況下, 每一份報表都要實作這三個流程 Template Method Patten作法是這樣的: Base class定義產生流程的順序 子類別負責準備這份報表所需資料 classDiagram class BaseReport { +Generate() #Init() #LoadTemplate() #PrefillContent() #ExportFile() #ConvertToPdfIfNeeded() } class ConcreteReportA { +Init() } class ConcreteReportB { +Init() } BaseReport <|-- ConcreteReportA BaseReport <|-- ConcreteReportB 關鍵在於子類別不能決定流程, 只能提供資料 ...

January 19, 2026 · 1 min · Yen Tsai

Vue3 Composition API

setup Composition API 功能要寫在setup裡面 <script> export defalut { setup(){ //... } } </script> 也可以寫成 <script setup> </script> ref 定義響應性數據 參數名稱要+.value來修改資料 <template> <h1>{{ title }}</h1> </template> <script setup> import { ref } from 'vue'; const title = ref('title'); title.value = 'new title'; </script>

September 25, 2023 · 1 min · Yen Tsai

MSSQL 無法連線至XXX 問題

以下可能原因: ...

September 1, 2023 · 1 min · Yen Tsai

使用Vue Teleport實作提示框功能

實作在右下角顯示多個提示框功能 修改index.html部分, 在body區域新增一個messages用來放置提示框的位置 //index.html <!doctype html> //... <body> <div id="app"></div> <div id="messages"></div> <script type="module" src="/src/main.js"></script> </body> //... ...

August 23, 2023 · 1 min · Yen Tsai

31. Next Permutation

題目 https://leetcode.com/problems/next-permutation/description/ A permutation of an array of integers is an arrangement of its members into a sequence or linear order. For example, for arr = [1,2,3], the following are all the permutations of arr: [1,2,3], [1,3,2], [2, 1, 3], [2, 3, 1], [3,1,2], [3,2,1]. The next permutation of an array of integers is the next lexicographically greater permutation of its integer. More formally, if all the permutations of the array are sorted in one container according to their lexicographical order, then the next permutation of that array is the permutation that follows it in the sorted container. If such arrangement is not possible, the array must be rearranged as the lowest possible order (i.e., sorted in ascending order). For example, the next permutation of arr = [1,2,3] is [1,3,2]. Similarly, the next permutation of arr = [2,3,1] is [3,1,2]. While the next permutation of arr = [3,2,1] is [1,2,3] because [3,2,1] does not have a lexicographical larger rearrangement. Given an array of integers nums, find the next permutation of nums. The replacement must be in place and use only constant extra memory. ...

May 29, 2023 · 2 min · Yen Tsai