iT邦幫忙

2026 iThome 鐵人賽

DAY 2
2

如果要用 JavaScript 儲存一群資料,我們其實很容易想到一個最直覺的答案:

const users = ["Alice", "Bob", "Charlie"];

需要增加資料?

users.push("David");

需要找其中一筆?

users.find(user => user === "Bob");

需要排序?

users.sort();

看起來 Array 幾乎什麼事情都能做。

那問題來了:

如果所有資料都可以塞進 Array,為什麼我們還需要 Queue、Stack、Tree、Graph,甚至 Heap、Hash Table 這麼多不同的資料結構?

這也是這個系列想從第一篇開始回答的問題。

因為資料結構真正處理的,從來不只是:

資料要放在哪裡?

而是:

這些資料彼此之間是什麼關係,還有我們要怎麼操作它們?


Array 的確什麼都能裝

假設今天有三個人在排隊:

const waitingList = [
  "Alice",
  "Bob",
  "Charlie"
];

第四個人加入隊伍:

waitingList.push("David");

輪到 Alice 時,我們把第一個人拿掉:

const next = waitingList.shift();

這段程式完全可以運作。
所以我們是不是根本不需要 Queue?

https://ithelp.ithome.com.tw/upload/images/20260828/20129020O5BZHnGgxG.jpg

因為 Queue 並不是因為 Array 做不到才存在的。

Queue 真正表達的是一個規則:

First In, First Out。

也就是:

先進來的人,先離開。

當我們說「這是一個 Queue」,我們其實已經對資料加上了一層語意。

資料不再單純表示:

Alice
Bob
Charlie

還要講順序:

進入順序
↓
Alice → Bob → Charlie
↑
離開順序

這個差異才是重點

Array 告訴我們:

我有一群資料。

Queue 告訴我們:

我有一群按照進入順序等待處理的資料。

資料可能完全相同,但問題的表示方式已經不同了。


資料結構其實是一種問題表述

這可能是整個系列最重要的一個觀念:

Data Structure is a form of problem representation.

我們選擇資料結構,其實是在決定:

我要用什麼方式描述眼前的問題?

例如今天我們有以下資料:

const people = [
  "Alice",
  "Bob",
  "Charlie",
  "David"
];

如果只是要保存姓名,Array 處理綽綽有餘。

但假設需求變成:

Alice 認識 Bob 和 Charlie,Bob 認識 David,Charlie 也認識 David。

現在問題的重點已經不再是「有哪些人」,還要能承載:

誰和誰有關係?

我們可能會開始把資料寫成:

const friends = {
  Alice: ["Bob", "Charlie"],
  Bob: ["Alice", "David"],
  Charlie: ["Alice", "David"],
  David: ["Bob", "Charlie"]
};

底層依然可以看見 Object 和 Array。
但我們真正描述的東西,其實已經變成:

Alice ─── Bob
  │        │
  │        │
Charlie ─ David

這就是 Graph 想表達的世界,Graph 並不是一種神奇的新資料。

它只是剛好適合去描述:

這個問題的核心,是節點之間的關係。


Tree 也是相同的道理

假設我們要表示公司的組織:

CEO
├─ Engineering
│  ├─ Frontend
│  └─ Backend
└─ Marketing

當然,我們還是可以把所有資料塞進 Array:

const departments = [
  "CEO",
  "Engineering",
  "Frontend",
  "Backend",
  "Marketing"
];

資料都在,但更重要的從屬關係消失了。
當 Frontend 是 Engineering 下面的部門,Engineering 又屬於 CEO。
這時候我們想要表達的是:

parent
  ↓
child

也就是階層關係,於是 Tree 自然出現了。
不是因為 Array 無法儲存這些字串,只是因為 Array 本身已經沒有辦法幫我們表達這種階層。

https://ithelp.ithome.com.tw/upload/images/20260828/20129020K1xmGeqUSr.png


不同的表示方式,會讓不同的事情變簡單

這也是為什麼不存在一種「最好的資料結構」。
假設今天我們一直需要:

找到下一個等待處理的人。

Queue 很直覺。

如果我們需要:

永遠先處理最高優先權的任務。

那 Priority Queue 或 Heap 可能更合理。

如果我們需要:

表達資料之間的從屬關係。

Tree 會很自然。

如果問題是:

從台北到高雄有哪些可能的路線?

那麼 Graph 可能比 Tree 更接近問題本身。

因此選擇資料結構時,我們真正問的不是:

哪一個比較快?

還是得先問:

我的問題到底長什麼樣子?

當問題被正確表示之後,「快不快」才開始有意義。


演算法為什麼總是和資料結構綁在一起?

我們學演算法時,幾乎一定會碰到資料結構。
因為演算法並不是憑空想捏造的操作步驟,它通常會建立在某種資料表示方式上。

例如我們之後會看到:

Graph
 ↓
BFS / DFS
 ↓
Shortest Path

又或者:

Heap
 ↓
Priority Queue
 ↓
Scheduling

甚至:

Tree
 ↓
Tree Traversal
 ↓
Search

資料結構決定:

問題被表示成什麼形狀。

演算法則決定:

我們準備怎麼在這個結構上移動、搜尋、修改或做決策。

所以很多時候,一個 hard level 演算法問題真正困難的地方,並不在考你那個演算法怎麼寫。
是在考你有沒有看出:

這個問題其實可以被表示成什麼。


那 Big-O 又是在做什麼?

講到資料結構和演算法,很快就會遇到另一個讓很多人頭痛的東西:

O(1)
O(log n)
O(n)
O(n²)

我們先不用急著證明它們,甚至暫時不用背。
現在只需要建立一個非常簡單的認知:

當資料越來越多時,我們做某件事情的成本會怎麼變?

假設只有 5 筆資料,從第一筆一路找下去,可能根本沒有感覺。
如果有:

10
100
1,000
1,000,000

筆資料呢?

資料增加之後,不同操作方式之間的差距才會慢慢被放大。
所以 Big-O 真正想幫我們描述的是一種:

成長趨勢。

迫使我們去思考:

如果問題規模變大,這個方法會變得多昂貴?

完整的複雜度比較我們之後再慢慢談。


資料結構不是「不同種類的容器」

如果只從語法看:

[]
{}
new Map()
new Set()

我們很容易把資料結構理解成:

JavaScript 提供了很多不同方式讓我們裝資料。

但這只是表面,裡面還是對映著:

Queue
Stack
Tree
Graph
Heap
Hash Table

每一種結構背後,其實都代表某一類問題的形狀:

  • Queue 在描述順序
  • Stack 在描述後進先出的操作方式
  • Tree 在描述階層
  • Graph 在描述關係與連結
  • Heap 在描述優先權
  • Hash Table 在處理如何快速透過 key 找到資料

所以學資料結構真正值得學的是去想:

什麼問題會自然形成 Queue?

如果只記得怎麼 call API,很快就會忘記。

但如果你理解它為什麼存在,那麼即使今天換成 JavaScript、Python、Go,甚至換了一套完全不同的 API,你仍然知道自己需要的是什麼。


Array 並沒有輸

所以回到一開始的問題:

如果所有東西都可以塞進 Array,為什麼還需要其他資料結構?

答案其實是:

因為「放得進去」和「適合描述這個問題」是兩件完全不同的事情。

我們當然可以用 Array 模擬 Queue,也可以用 Object 和 Array 建立 Graph,甚至可以自己決定怎麼表示一棵 Tree。

最重要的還是:

你是否理解這些資料之間真正存在的關係。

這也是接下來這個系列真正想討論的事情。

我們不會只是背:

Queue 是 FIFO。
Stack 是 LIFO。
BFS 使用 Queue。
DFS 使用 Stack。

是學會一直追問:

為什麼?

  • 為什麼排隊形成 Queue?
  • 為什麼瀏覽器歷史紀錄可以看成 Stack?
  • 為什麼檔案系統很容易形成 Tree?
  • 為什麼社群網站、地圖與依賴關係最後常常變成 Graph?

當你開始看見這些結構,演算法就不再只是 LeetCode 裡的一道題目,它開始變成一種理解現實問題的方法。

而這,才是我們真正需要資料結構的原因。


小結

今天先留下四個觀念:

  1. 資料結構是一種 Problem Representation。
  2. 不同表示方式,會讓不同操作變簡單或困難。
  3. Algorithm 通常建立在某種 Data Structure 之上。
  4. Big-O 可以先理解成:資料增加時,操作成本如何成長。

下一篇,我們就可以從最熟悉的 Array 開始。

因為即使是每天都在使用的 Array,背後其實也藏著一個很值得問的問題:

為什麼「按照位置取得資料」這件事情,可以這麼直覺?


上一篇
當 AI 已經會寫程式,我們為什麼還要學資料結構與演算法?
下一篇
Day 2|排隊為什麼需要 Queue?
系列文
生活中的資料結構與演算法:30 天學會把現實問題變成可推理的模型3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言