Menu
CoddyTech

Hash map

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

Hash map lưu các cặp khóa-giá trị và cho phép bạn tra cứu một giá trị theo khóa của nó trong trung bình O(1). Nó hoạt động giống hệt bảng băm (hash table), nhưng mỗi mục trong bucket mang cả khóa lẫn giá trị đi kèm. Để lưu hoặc lấy một cặp, khóa được băm thành chỉ số bucket, rồi chuỗi của bucket đó được duyệt để tìm khóa khớp. Nhấn Phát ở trên để xem các cặp được đặt vào bucket theo giá trị băm và một giá trị được lấy ra theo khóa.

Đây là cấu trúc đứng sau dict của Python, HashMap của Java và Map/object của JavaScript. Xung đột được xử lý giống như trong bảng băm, ở đây là bằng nối chuỗi (separate chaining), nên hiệu năng phụ thuộc vào một hàm băm tốt và hệ số tải thấp.

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

Thao tácTrung bìnhXấu nhất
Put (chèn/cập nhật)O(1)O(n)
Get (tra cứu)O(1)O(n)
XóaO(1)O(n)
Không gianO(n)O(n)

Hash map trong các ngôn ngữ phổ biến

Ngôn ngữKiểu
Pythondict
JavaHashMap
JavaScriptMap / object
C++std::unordered_map
Gomap

Ví dụ minh họa

Chèn các cặp ("cat", 3), ("dog", 5), ("cat", 9), ("emu", 7) vào một map có 8 bucket, dùng hash(key) % 8. Giả sử hash("cat") % 8 = 2, hash("dog") % 8 = 5, hash("emu") % 8 = 2:

BướcCấu trúcThao tác
Put ("cat", 3)bucket 2: [("cat", 3)]Băm vào bucket 2; chuỗi đang rỗng, nên thêm cặp vào.
Put ("dog", 5)bucket 2: [("cat", 3)], bucket 5: [("dog", 5)]Băm vào bucket 5; chuỗi đang rỗng, nên thêm cặp vào.
Put ("cat", 9)bucket 2: [("cat", 9)], bucket 5: [("dog", 5)]Băm vào bucket 2; khóa "cat" đã có trong chuỗi, nên cập nhật giá trị thành 9.
Put ("emu", 7)bucket 2: [("cat", 9), ("emu", 7)], bucket 5: [("dog", 5)]Băm vào bucket 2; xung đột với "cat", không tìm thấy khóa, nên thêm cặp vào.
Get "emu"bucket 2: [("cat", 9), ("emu", 7)]Băm vào bucket 2; duyệt chuỗi, bỏ qua "cat", khớp "emu", trả về 7.

Khi nào nên dùng hash map

Nên dùng khiTránh dùng khi
Bạn cần tra cứu, chèn và xóa theo đúng 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 hoặc truy vấn theo khoảng: hãy dùng BST cân bằng hoặc sorted map.
Khóa băm được và phép so sánh bằng được định nghĩa rõ ràng (chuỗi, số nguyên, tuple).Khóa có thể thay đổi sau khi chèn, làm hỏng vị trí bucket của nó.
Bạn đang đếm, loại bỏ trùng lặp, lưu cache hoặc đánh chỉ mục theo một mã định danh.Bạn cần bảo đảm O(log n) trong trường hợp xấu nhất thay vì cận trung bình phân bổ.
Thứ tự duyệt không quan trọng, hoặc một map giữ thứ tự chèn là đủ.Bộ nhớ rất eo hẹp: bucket, khoảng trống dự phòng cho hệ số tải và con trỏ đều tốn thêm bộ nhớ.

Code Hash Map

Một cách cài đặt Hash Map 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 Map bằng Python

Python
1# Python's built-in dict is a hash map: O(1) average2# insert, lookup, and delete.3inventory = {"apple": 3, "banana": 7}4
5# Insert and update6inventory["cherry"] = 57inventory["apple"] += 28
9# Lookup, with .get for a safe default on missing keys10print("apple: ", inventory["apple"])11print("mango: ", inventory.get("mango", 0))12
13# Membership test and delete14print("banana in stock:", "banana" in inventory)15del inventory["banana"]16print("banana in stock:", "banana" in inventory)17
18# Iterate over key-value pairs19for fruit, count in sorted(inventory.items()):20    print(f"{fruit}: {count}")21
22# Classic hash map use case: counting frequencies23words = "the quick brown fox jumps over the lazy dog the end".split()24freq = {}25for word in words:26    freq[word] = freq.get(word, 0) + 127
28print("Occurrences of the:", freq["the"])29print("Most common word:  ", max(freq, key=freq.get))
Chạy code này trong Playground Python

Câu hỏi thường gặp về hash map

Hash map và bảng băm khác nhau như thế nào?

Về cơ bản chúng là cùng một cấu trúc: một mảng các bucket được đánh địa chỉ bằng giá trị băm của khóa. Trong cách dùng thông thường, "bảng băm" (hash table) thường chỉ một tập các khóa hoặc kỹ thuật nói chung, còn "hash map" nhấn mạnh việc lưu cặp khóa-giá trị. Một số ngôn ngữ còn phân biệt về an toàn luồng (ví dụ Hashtable và HashMap trong Java), nhưng thuật toán cốt lõi là như nhau.

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

Nó giải quyết xung đột. Mô phỏng này dùng nối chuỗi: mỗi bucket giữ một danh sách nhỏ, và cặp mới có khóa băm vào đó sẽ được thêm vào danh sách. Khi tra cứu, map duyệt chuỗi ngắn đó để tìm khóa khớp. Kỹ thuật thay thế là địa chỉ mở (open addressing), dò tìm một ô khác trong mảng.

Hash map hay cây tìm kiếm nhị phân: nên dùng cái nào?
Dùng hash map khi bạn chỉ cần tra cứu theo đúng khóa và muốn các thao tác trung bình O(1). Dùng cây tìm kiếm nhị phân cân bằng (như TreeMap hoặc std::map) 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, với chi phí O(log n). BST đánh đổi một chút tốc độ để có bảo đảm về thứ tự mà hash map không cung cấp được.
Hệ số tải là gì và vì sao nó kích hoạt việc thay đổi kích thước?
Hệ số tải (load factor) là tỷ lệ giữa số mục đã lưu và số bucket. Khi nó tăng, các chuỗi dài ra và tra cứu trung bình chậm đi, nên phần lớn các bản cài đặt sẽ thay đổi kích thước (thường là gấp đôi số bucket và băm lại mọi thứ) khi vượt ngưỡng như 0.75. Thay đổi kích thước tốn O(n) nhưng hiếm khi xảy ra, nên chi phí được phân bổ đều và các thao tác trung bình vẫn là O(1).
Có thể dùng đối tượng thay đổi được như list làm khóa của hash map không?
Thường là không. Khóa phải băm được và giá trị băm phải giữ nguyên trong suốt thời gian nằm trong map; ví dụ Python báo TypeError: unhashable type với một list. Nếu bạn thay đổi khóa sau khi chèn, giá trị băm của nó đổi theo và map không còn tìm thấy nó trong đúng bucket, khiến mục đó bị mất mà không có thông báo nào. Hãy dùng khóa bất biến như chuỗi, số hoặc tuple.
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