資料結構概論

程式 = 資料結構 + 演算法。

選擇正確的資料結構,往往比寫出完美的演算法更重要——因為資料結構決定了你的程式能跑多快、花多少記憶體

🔥 Vibe Prompt

「用 Python 實作以下資料結構並比較效能:動態陣列 (Dynamic Array)、雙向鏈結串列 (Doubly Linked List)、雜湊表 (Hash Table) 與二元搜尋樹 (BST)。每個結構至少包含插入、刪除、搜尋操作,並用 10000 筆資料測試時間複雜度。」

陣列 (Array)

最基礎的資料結構。元素在記憶體中連續存放,可透過索引 O(1) 存取。

| 操作 | 時間複雜度 | 說明 | |------|-----------|------| | 存取 (Access) | O(1) | 透過索引直接取得 | | 搜尋 (Search) | O(n) | 需要遍歷(除非已排序) | | 插入 (Insert) | O(n) | 需要移動後續元素 | | 刪除 (Delete) | O(n) | 需要移動後續元素 |

# Python 動態陣列實作
class DynamicArray:
    def __init__(self):
        self.capacity = 1
        self.size = 0
        self.array = [None] * self.capacity
    
    def _resize(self, new_capacity):
        new_array = [None] * new_capacity
        for i in range(self.size):
            new_array[i] = self.array[i]
        self.array = new_array
        self.capacity = new_capacity
    
    def append(self, item):
        if self.size == self.capacity:
            self._resize(2 * self.capacity)
        self.array[self.size] = item
        self.size += 1
    
    def insert(self, index, item):
        if index < 0 or index > self.size:
            raise IndexError("Index out of range")
        if self.size == self.capacity:
            self._resize(2 * self.capacity)
        for i in range(self.size, index, -1):
            self.array[i] = self.array[i - 1]
        self.array[index] = item
        self.size += 1
    
    def delete(self, index):
        if index < 0 or index >= self.size:
            raise IndexError("Index out of range")
        for i in range(index, self.size - 1):
            self.array[i] = self.array[i + 1]
        self.array[self.size - 1] = None
        self.size -= 1
    
    def __getitem__(self, index):
        if index < 0 or index >= self.size:
            raise IndexError("Index out of range")
        return self.array[index]
    
    def __len__(self):
        return self.size

# 測試
da = DynamicArray()
for i in range(10):
    da.append(i)
print(f"陣列內容: {[da[i] for i in range(len(da))]}")
print(f"容量: {da.capacity}, 大小: {len(da)}")
da.insert(0, 100)
print(f"插入後: {[da[i] for i in range(len(da))]}")
da.delete(5)
print(f"刪除後: {[da[i] for i in range(len(da))]}")

鏈結串列 (Linked List)

元素不連續存放,每個節點包含資料與指向下一個節點的指標。

| 操作 | 時間複雜度 | 說明 | |------|-----------|------| | 存取 (Access) | O(n) | 需要從頭遍歷 | | 搜尋 (Search) | O(n) | 需要遍歷 | | 插入 (Insert) | O(1) | 只需修改指標(已知位置) | | 刪除 (Delete) | O(1) | 只需修改指標(已知位置) |

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None
        self.prev = None

class DoublyLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.size = 0
    
    def append(self, data):
        node = Node(data)
        if not self.head:
            self.head = self.tail = node
        else:
            self.tail.next = node
            node.prev = self.tail
            self.tail = node
        self.size += 1
    
    def prepend(self, data):
        node = Node(data)
        if not self.head:
            self.head = self.tail = node
        else:
            node.next = self.head
            self.head.prev = node
            self.head = node
        self.size += 1
    
    def delete(self, data):
        current = self.head
        while current:
            if current.data == data:
                if current.prev:
                    current.prev.next = current.next
                else:
                    self.head = current.next
                if current.next:
                    current.next.prev = current.prev
                else:
                    self.tail = current.prev
                self.size -= 1
                return True
            current = current.next
        return False
    
    def __iter__(self):
        current = self.head
        while current:
            yield current.data
            current = current.next
    
    def __len__(self):
        return self.size

# 測試
dll = DoublyLinkedList()
dll.append(1)
dll.append(2)
dll.append(3)
dll.prepend(0)
print(f"鏈結串列: {list(dll)}")  # [0, 1, 2, 3]
dll.delete(2)
print(f"刪除 2 後: {list(dll)}")  # [0, 1, 3]

雜湊表 (Hash Table)

透過雜湊函數將鍵映射到陣列索引,達到平均 O(1) 的存取速度。

| 操作 | 平均 | 最差 | |------|------|------| | 存取 | O(1) | O(n) | | 搜尋 | O(1) | O(n) | | 插入 | O(1) | O(n) | | 刪除 | O(1) | O(n) |

class HashTable:
    def __init__(self, capacity=16):
        self.capacity = capacity
        self.size = 0
        self.buckets = [[] for _ in range(capacity)]
    
    def _hash(self, key):
        return hash(key) % self.capacity
    
    def _resize(self):
        old_buckets = self.buckets
        self.capacity *= 2
        self.buckets = [[] for _ in range(self.capacity)]
        self.size = 0
        for bucket in old_buckets:
            for k, v in bucket:
                self.put(k, v)
    
    def put(self, key, value):
        if self.size >= self.capacity * 0.75:
            self._resize()
        idx = self._hash(key)
        for i, (k, _) in enumerate(self.buckets[idx]):
            if k == key:
                self.buckets[idx][i] = (key, value)
                return
        self.buckets[idx].append((key, value))
        self.size += 1
    
    def get(self, key):
        idx = self._hash(key)
        for k, v in self.buckets[idx]:
            if k == key:
                return v
        raise KeyError(key)
    
    def delete(self, key):
        idx = self._hash(key)
        for i, (k, _) in enumerate(self.buckets[idx]):
            if k == key:
                del self.buckets[idx][i]
                self.size -= 1
                return
        raise KeyError(key)
    
    def __contains__(self, key):
        try:
            self.get(key)
            return True
        except KeyError:
            return False

# 測試
ht = HashTable()
ht.put("name", "Alice")
ht.put("age", 25)
ht.put("city", "Taipei")
print(f"name = {ht.get('name')}")
print(f"age = {ht.get('age')}")
print(f""city" 是否存在: {'city' in ht}")
ht.delete("age")
print(f""age" 是否存在: {'age' in ht}")

二元搜尋樹 (Binary Search Tree)

每個節點最多有兩個子節點:左子節點的值 < 父節點 < 右子節點的值。

| 操作 | 平均 | 最差(歪斜樹) | |------|------|---------------| | 搜尋 | O(log n) | O(n) | | 插入 | O(log n) | O(n) | | 刪除 | O(log n) | O(n) |

class TreeNode:
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None

class BinarySearchTree:
    def __init__(self):
        self.root = None
    
    def insert(self, val):
        if not self.root:
            self.root = TreeNode(val)
            return
        current = self.root
        while True:
            if val < current.val:
                if current.left:
                    current = current.left
                else:
                    current.left = TreeNode(val)
                    break
            else:
                if current.right:
                    current = current.right
                else:
                    current.right = TreeNode(val)
                    break
    
    def search(self, val):
        current = self.root
        while current and current.val != val:
            if val < current.val:
                current = current.left
            else:
                current = current.right
        return current is not None
    
    def inorder(self, node=None, result=None):
        if result is None:
            result = []
        if node is None:
            node = self.root
        if node:
            self.inorder(node.left, result)
            result.append(node.val)
            self.inorder(node.right, result)
        return result

# 測試
bst = BinarySearchTree()
for v in [5, 3, 7, 2, 4, 6, 8]:
    bst.insert(v)
print(f"中序遍歷: {bst.inorder()}")  # [2, 3, 4, 5, 6, 7, 8]
print(f"搜尋 4: {bst.search(4)}")
print(f"搜尋 9: {bst.search(9)}")

如何選擇?

| 資料結構 | 適合場景 | 不適合場景 | |---------|---------|-----------| | 陣列 | 隨機存取多、大小固定 | 頻繁插入/刪除 | | 鏈結串列 | 頻繁插入/刪除、大小不固定 | 需要隨機存取 | | 雜湊表 | 鍵值對查詢、去重 | 需要有序遍歷 | | 二元搜尋樹 | 需要有序資料、範圍查詢 | 資料量極大(需平衡樹) |

總結

  • 陣列: 隨機存取 O(1),但插入/刪除慢
  • 鏈結串列: 插入/刪除 O(1),但隨機存取慢
  • 雜湊表: 平均 O(1) 查詢,不保留順序
  • 二元搜尋樹: O(log n) 操作,保留排序

實戰練習

💡 Vibe Coding 練習:請 AI 建立一個「資料結構效能比較工具」:

  1. 對陣列、鏈結串列、雜湊表、BST 進行 10000 次插入/搜尋/刪除操作
  2. 精確測量每種操作的時間
  3. 輸出比較圖表
  4. 分析每種資料結構的適用場景

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!