Menu
CoddyTech

Bảng băm (hash table)

Cập nhật lần cuối

Bảng băm (hash table) lưu các phần tử trong một mảng các bucket. Để đặt hoặc tìm một khóa, bảng băm đưa khóa qua hàm băm và lấy kết quả chia lấy dư cho số bucket, cho ra chỉ số bucket trong O(1). Nhấn Phát ở trên để xem từng khóa được băm vào một bucket và lưu lại, rồi một lần tra cứu nhảy thẳng đến đúng bucket.

Khi hai khóa băm vào cùng một bucket, gọi là xung đột (collision), mô phỏng này dùng phương pháp nối chuỗi (separate chaining): mỗi bucket giữ một danh sách nhỏ, và các khóa xung đột được thêm vào đó. Miễn là bảng không quá đầy (hệ số tải thấp) và hàm băm phân tán khóa tốt, các chuỗi sẽ ngắn và việc chèn, tra cứu, xóa đều là O(1) trung bình.

Độ phức tạp thời gian

Thao tácTrung bìnhXấu nhất
ChènO(1)O(n) (mọi khóa đều xung đột)
Tra cứuO(1)O(n)
XóaO(1)O(n)
Không gianO(n)O(n)

Các khái niệm chính

Thuật ngữÝ nghĩa
Hàm bămÁnh xạ một khóa thành chỉ số bucket
Xung độtHai khóa rơi vào cùng một bucket
Nối chuỗiMỗi bucket giữ một danh sách các mục bị xung đột
Địa chỉ mởCách thay thế: dò tìm ô trống tiếp theo
Hệ số tảisố mục / số bucket, quyết định khi nào thay đổi kích thước

Ví dụ minh họa

Chèn các khóa 20, 34, 9, 13 vào một bảng có 7 bucket, dùng hash(k) = k % 7:

BướcCấu trúcThao tác
Chèn 20bucket 6: [20]20 % 7 = 6, bucket 6 đang trống, lưu 20
Chèn 34bucket 6: [20, 34]34 % 7 = 6, xung đột với 20, thêm 34 vào chuỗi
Chèn 9bucket 2: [9]9 % 7 = 2, bucket 2 đang trống, lưu 9
Chèn 13bucket 6: [20, 34, 13]13 % 7 = 6, xung đột, thêm 13 vào chuỗi của bucket 6
Tra cứu 34bucket 6: [20, 34, 13]34 % 7 = 6, duyệt chuỗi: 20 không khớp, 34 khớp, đã tìm thấy

Khi nào nên dùng bảng băm

Nên dùng khiTránh dùng khi
Bạn cần chèn, tra cứu và xóa theo khóa với chi phí trung bình O(1)Bạn cần giữ khóa theo thứ tự đã sắp xếp: hãy dùng BST cân bằng
Khóa không có thứ tự hữu ích và bạn chỉ kiểm tra sự tồn tại theo đúng giá trịBạn cần truy vấn theo khoảng hoặc tìm khóa gần nhất
Bạn có thể chấp nhận thêm chút bộ nhớ cho các bucket và hệ số tải thấpBộ nhớ cực kỳ eo hẹp và từng byte đều quan trọng
Có sẵn một hàm băm tốt cho kiểu khóa của bạnĐộ trễ trong trường hợp xấu nhất phải có giới hạn: xung đột khiến nó thành O(n)

Code Hash Table

Một cách cài đặt Hash Table gọn gàng, chạy được ngay bằng Python, JavaScript, Java, C++, C. Chọn một ngôn ngữ, sao chép code hoặc mở sẵn trong Coddy Playground.

Code Hash Table bằng Python

Python
1class HashTable:2    def __init__(self, size=8):3        self.size = size4        self.buckets = [[] for _ in range(size)]5
6    def _index(self, key):7        # Hash the key to a bucket; different keys can collide8        return sum(ord(ch) for ch in key) % self.size9
10    def set(self, key, value):11        bucket = self.buckets[self._index(key)]12        for i, (k, _) in enumerate(bucket):13            if k == key:14                bucket[i] = (key, value)  # update existing key15                return16        bucket.append((key, value))  # chain on collision17
18    def get(self, key):19        for k, v in self.buckets[self._index(key)]:20            if k == key:21                return v22        raise KeyError(key)23
24    def delete(self, key):25        bucket = self.buckets[self._index(key)]26        for i, (k, _) in enumerate(bucket):27            if k == key:28                del bucket[i]29                return30        raise KeyError(key)31
32
33table = HashTable()34table.set("apple", 3)35table.set("banana", 7)36table.set("cherry", 5)37
38print("apple  ->", table.get("apple"))39print("banana ->", table.get("banana"))40table.set("apple", 10)41print("apple  ->", table.get("apple"))42table.delete("banana")43print("bucket sizes:", [len(b) for b in table.buckets])
Chạy code này trong Playground Python

Câu hỏi thường gặp về bảng băm

Xung đột băm là gì và được giải quyết như thế nào?

Xung đột xảy ra khi hai khóa khác nhau băm vào cùng một bucket. Hai cách xử lý phổ biến là nối chuỗi (mỗi bucket giữ một danh sách và các khóa xung đột được thêm vào, như minh họa ở đây) và địa chỉ mở (dò tìm ô trống tiếp theo trong mảng). Cả hai đều giữ việc tra cứu chính xác; chúng khác nhau ở cách bố trí bộ nhớ và hiệu năng khi tải cao.

Độ phức tạp thời gian của bảng băm là bao nhiêu?
Chèn, tra cứu và xóa đều là O(1) trung bình khi hàm băm phân bố khóa đều và hệ số tải được giữ thấp. Trong trường hợp xấu nhất, khi mọi khóa đều xung đột vào một bucket, các thao tác suy biến thành O(n), vì vậy hàm băm tốt và việc thay đổi kích thước rất quan trọng.
Hệ số tải là gì?

Hệ số tải (load factor) là số mục đã lưu chia cho số bucket. Khi nó tăng, các chuỗi dài ra và thao tác chậm đi, nên phần lớn bảng băm sẽ thay đổi kích thước (băm lại vào một mảng lớn hơn) khi vượt ngưỡng như 0,75.

Khi nào nên dùng bảng băm thay cho cây tìm kiếm nhị phân?
Dùng bảng băm khi bạn chỉ cần tra cứu theo đúng giá trị và muốn tốc độ trung bình O(1) mà không cần thứ tự. Dùng cây tìm kiếm nhị phân cân bằng khi bạn cần khóa theo thứ tự đã sắp xếp, truy vấn theo khoảng hoặc tìm phần tử liền trước/liền sau, những việc bảng băm không làm hiệu quả được. BST bảo đảm các thao tác O(log n), còn bảng băm đánh đổi bảo đảm đó để có hiệu năng trung bình nhanh hơn.
Nối chuỗi và địa chỉ mở khác nhau như thế nào?

Nối chuỗi lưu các khóa xung đột trong một danh sách riêng cho mỗi bucket, nên một bucket có thể chứa nhiều mục và bảng không bao giờ thực sự đầy. Địa chỉ mở giữ mọi thứ ngay trong mảng và dò tìm ô trống tiếp theo khi xung đột, cách này thân thiện với bộ nhớ đệm nhưng hiệu năng giảm mạnh khi hệ số tải tiến gần 1 và cần xử lý việc xóa cẩn thận. Nối chuỗi chịu được hệ số tải cao hơn; địa chỉ mở dùng bộ nhớ gọn hơn.

Vì sao không thể trông cậy vào bảng băm để giữ khóa theo thứ tự chèn hoặc thứ tự đã sắp xếp?
Hàm băm cố ý rải các khóa ra nhiều bucket để tránh dồn cụm, nên thứ tự duyệt phản ánh cách bố trí bucket, không phải thứ tự chèn hay thứ tự sắp xếp. Nếu bạn cần thứ tự, hãy dùng map/cây có thứ tự hoặc một cấu trúc như map giữ thứ tự chèn (ví dụ dict của Python giữ thứ tự chèn, nhưng đó là bảo đảm của ngôn ngữ, không phải tính chất vốn có của bảng băm). Đừng bao giờ giả định thứ tự duyệt khớp với cách bạn chèn khóa trừ khi ngôn ngữ cam kết rõ ràng điều đó.
Hình minh họa các ngôn ngữ lập trình của Coddy

Thành thạo thuật toán cùng Coddy

Bắt đầu