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 的核心精神
什麼是 Array?
定義:Array(陣列)是一種將「同類型」的資料,存放在「連續」記憶體空間的資料結構
這個定義有兩個重點:
- 同類型 (Homogeneous):每個元素需要的記憶體大小固定且相同
- 連續記憶體 (Contiguous Memory):資料在記憶體中是緊密排列的,中間沒有空隙
為什麼要用 Array?
當我們將資料整理成 Array 結構後,獲得了以下優勢:
- 有順序性:資料依然保持排列順序
- 支援隨機存取 (Random Access):這是 Array 最強大的特性,可以透過 Index 瞬間找到資料
- 適合批次操作:可以用迴圈輕鬆遍歷所有資料
Array 的記憶體原理
為什麼 Array 可以快速 (O(1)) 找到第 i 個資料?因為資料是連續存放的
電腦只需要知道:
- 起始位址 (Base Address)
- 索引 (Index)
- 單一資料大小 (Data Size)
就能算出目標位址:
$$ \text{Address}(i) = \text{Base\_Address} + (i \times \text{Data\_Size}) $$
程式碼驗證
讓我們實際用 C++ 觀察記憶體位址:
#include <iostream>
int main() {
// 定義一個整數陣列
int arr[5] = {10, 20, 30, 40, 50};
std::cout << "觀察陣列在記憶體中的分佈:" << std::endl;
// int 通常佔 4 bytes
for (int i = 0; i < 5; i++) {
std::cout << "arr[" << i << "] 的位址: " << &arr[i] << std::endl;
}
return 0;
}
輸出:
觀察陣列在記憶體中的分佈:
arr[0] 的位址: 0x7ffe6b849e60
arr[1] 的位址: 0x7ffe6b849e64 <-- +4 bytes
arr[2] 的位址: 0x7ffe6b849e68 <-- +4 bytes
arr[3] 的位址: 0x7ffe6b849e6c <-- +4 bytes
arr[4] 的位址: 0x7ffe6b849e70 <-- +4 bytes
你會發現,記憶體位址是連續的,每個間隔剛好是 4 bytes
這就是為什麼電腦不需要從頭數,直接用算的就能跳到目標位置
Array 的時間複雜度
| 操作 | 複雜度 | 說明 |
|---|---|---|
| 讀取 (Read) | O(1) | 透過公式直接計算位址,速度最快 |
| 插入 (Insert) | O(n) | 因為記憶體是連續的,若在中間插入一個新元素,後面的所有元素都要往後搬移一格,騰出空間 |
| 刪除 (Delete) | O(n) | 若刪除中間的元素,後面的所有元素都要往前補齊空缺,保持連續性 |
使用 Array 的最佳場景
- 資料量已知或固定:不需要頻繁擴展大小
- 讀多寫少:頻繁查詢某個位置的資料,但很少在中間插入或刪除
- 需要快速存取:依賴 Index 快速取得內容