Trong hướng dẫn này, bạn sẽ tìm hiểu bảng băm là gì. Ngoài ra, bạn sẽ tìm thấy các ví dụ làm việc về các phép toán bảng băm trong C, C ++, Java và Python.
Bảng băm là một cấu trúc dữ liệu đại diện cho dữ liệu dưới dạng các cặp khóa-giá trị . Mỗi khóa được ánh xạ tới một giá trị trong bảng băm. Các khóa được sử dụng để lập chỉ mục các giá trị / dữ liệu. Một cách tiếp cận tương tự được áp dụng bởi một mảng kết hợp.
Dữ liệu được biểu diễn trong một cặp giá trị khóa với sự trợ giúp của các phím như trong hình bên dưới. Mỗi dữ liệu được liên kết với một khóa. Khóa là một số nguyên trỏ đến dữ liệu.
1. Bảng địa chỉ trực tiếp
Bảng địa chỉ trực tiếp được sử dụng khi dung lượng của bảng được sử dụng không phải là vấn đề đối với chương trình. Ở đây, chúng tôi giả định rằng
- các phím là số nguyên nhỏ
- số lượng khóa không quá lớn và
- không có hai dữ liệu nào có cùng một khóa
Một nhóm các số nguyên được gọi là vũ trụ U = (0, 1,… ., n-1)
.
Mỗi vị trí của bảng địa chỉ trực tiếp T(0… n-1)
chứa một con trỏ đến phần tử tương ứng với dữ liệu.
Chỉ mục của mảng T
là chính khóa và nội dung của T
là một con trỏ đến tập hợp (key, element)
. Nếu không có phần tử nào cho một khóa thì nó được để là NULL
.
Đôi khi, chìa khóa chính là dữ liệu.
Mã giả cho các hoạt động
directAddressSearch(T, k) return T(k) directAddressInsert(T, x) T(x.key) = x directAddressDelete(T, x) T(x.key) = NIL
Hạn chế của bảng địa chỉ trực tiếp
- Giá trị của khóa phải nhỏ.
- Số lượng khóa phải đủ nhỏ để nó không vượt qua giới hạn kích thước của một mảng.
2. Bảng băm
Trong bảng băm, các khóa được xử lý để tạo ra một chỉ mục mới ánh xạ đến phần tử được yêu cầu. Quá trình này được gọi là băm.
Hãy h(x)
là một hàm băm và k
là một khóa.h(k)
được tính toán và nó được sử dụng làm chỉ số cho phần tử.
Hạn chế của bảng băm
- Nếu cùng một chỉ mục được tạo ra bởi hàm băm cho nhiều khóa thì xung đột sẽ phát sinh. Tình huống này được gọi là va chạm.
Để tránh điều này, một hàm băm phù hợp được chọn. Nhưng, không thể tạo ra tất cả các khóa duy nhất bởi vì|U|>m
. Do đó, một hàm băm tốt có thể không ngăn chặn hoàn toàn các va chạm nhưng nó có thể làm giảm số lượng các va chạm.
Tuy nhiên, chúng tôi có các kỹ thuật khác để giải quyết va chạm.
Ưu điểm của bảng băm so với bảng địa chỉ trực tiếp:
Các vấn đề chính với bảng địa chỉ trực tiếp là kích thước của mảng và giá trị có thể lớn của một khóa. Hàm băm làm giảm phạm vi của chỉ mục và do đó kích thước của mảng cũng giảm.
Ví dụ, If k = 9845648451321
, then h(k) = 11
(bằng cách sử dụng một số hàm băm). Điều này giúp tiết kiệm bộ nhớ bị lãng phí trong khi cung cấp chỉ mục của 9845648451321
mảng
Giải quyết va chạm bằng cách xâu chuỗi
Trong kỹ thuật này, nếu một hàm băm tạo ra cùng một chỉ mục cho nhiều phần tử, các phần tử này được lưu trữ trong cùng một chỉ mục bằng cách sử dụng danh sách được liên kết kép.
Nếu j
là vị trí cho nhiều phần tử, nó chứa một con trỏ đến đầu danh sách các phần tử. Nếu không có phần tử nào, j
chứa NIL
.
Mã giả cho các hoạt động
chainedHashSearch(T, k) return T(h(k)) chainedHashInsert(T, x) T(h(x.key)) = x //insert at the head chainedHashDelete(T, x) T(h(x.key)) = NIL
Triển khai Python, Java, C và C ++
Python Java C C ++ # Python program to demonstrate working of HashTable hashTable = ((),) * 10 def checkPrime(n): if n == 1 or n == 0: return 0 for i in range(2, n//2): if n % i == 0: return 0 return 1 def getPrime(n): if n % 2 == 0: n = n + 1 while not checkPrime(n): n += 2 return n def hashFunction(key): capacity = getPrime(10) return key % capacity def insertData(key, data): index = hashFunction(key) hashTable(index) = (key, data) def removeData(key): index = hashFunction(key) hashTable(index) = 0 insertData(123, "apple") insertData(432, "mango") insertData(213, "banana") insertData(654, "guava") print(hashTable) removeData(123) print(hashTable)
// Java program to demonstrate working of HashTable import java.util.*; class HashTable ( public static void main(String args()) ( Hashtable ht = new Hashtable(); ht.put(123, 432); ht.put(12, 2345); ht.put(15, 5643); ht.put(3, 321); ht.remove(12); System.out.println(ht); ) )
// Implementing hash table in C #include #include struct set ( int key; int data; ); struct set *array; int capacity = 10; int size = 0; int hashFunction(int key) ( return (key % capacity); ) int checkPrime(int n) ( int i; if (n == 1 || n == 0) ( return 0; ) for (i = 2; i < n / 2; i++) ( if (n % i == 0) ( return 0; ) ) return 1; ) int getPrime(int n) ( if (n % 2 == 0) ( n++; ) while (!checkPrime(n)) ( n += 2; ) return n; ) void init_array() ( capacity = getPrime(capacity); array = (struct set *)malloc(capacity * sizeof(struct set)); for (int i = 0; i < capacity; i++) ( array(i).key = 0; array(i).data = 0; ) ) void insert(int key, int data) ( int index = hashFunction(key); if (array(index).data == 0) ( array(index).key = key; array(index).data = data; size++; printf(" Key (%d) has been inserted ", key); ) else if (array(index).key == key) ( array(index).data = data; ) else ( printf(" Collision occured "); ) ) void remove_element(int key) ( int index = hashFunction(key); if (array(index).data == 0) ( printf(" This key does not exist "); ) else ( array(index).key = 0; array(index).data = 0; size--; printf(" Key (%d) has been removed ", key); ) ) void display() ( int i; for (i = 0; i < capacity; i++) ( if (array(i).data == 0) ( printf(" array(%d): / ", i); ) else ( printf(" key: %d array(%d): %d ", array(i).key, i, array(i).data); ) ) ) int size_of_hashtable() ( return size; ) int main() ( int choice, key, data, n; int c = 0; init_array(); do ( printf("1.Insert item in the Hash Table" "2.Remove item from the Hash Table" "3.Check the size of Hash Table" "4.Display a Hash Table" " Please enter your choice: "); scanf("%d", &choice); switch (choice) ( case 1: printf("Enter key -: "); scanf("%d", &key); printf("Enter data -: "); scanf("%d", &data); insert(key, data); break; case 2: printf("Enter the key to delete-:"); scanf("%d", &key); remove_element(key); break; case 3: n = size_of_hashtable(); printf("Size of Hash Table is-:%d", n); break; case 4: display(); break; default: printf("Invalid Input"); ) printf("Do you want to continue (press 1 for yes): "); scanf("%d", &c); ) while (c == 1); )
// Implementing hash table in C++ #include #include using namespace std; class HashTable ( int capacity; list *table; public: HashTable(int V); void insertItem(int key, int data); void deleteItem(int key); int checkPrime(int n) ( int i; if (n == 1 || n == 0) ( return 0; ) for (i = 2; i capacity = size; table = new list(capacity); ) void HashTable::insertItem(int key, int data) ( int index = hashFunction(key); table(index).push_back(data); ) void HashTable::deleteItem(int key) ( int index = hashFunction(key); list::iterator i; for (i = table(index).begin(); i != table(index).end(); i++) ( if (*i == key) break; ) if (i != table(index).end()) table(index).erase(i); ) void HashTable::displayHash() ( for (int i = 0; i < capacity; i++) ( cout << "table(" << i << ")"; for (auto x : table(i)) cout < " << x; cout << endl; ) ) int main() ( int key() = (231, 321, 212, 321, 433, 262); int data() = (123, 432, 523, 43, 423, 111); int size = sizeof(key) / sizeof(key(0)); HashTable h(size); for (int i = 0; i < n; i++) h.insertItem(key(i), data(i)); h.deleteItem(12); h.displayHash(); )
Hàm băm tốt
Một hàm băm tốt có các đặc điểm sau.
- Nó không được tạo ra các khóa quá lớn và không gian nhóm nhỏ. Không gian bị lãng phí.
- Các phím được tạo không được gần cũng không quá xa trong phạm vi.
- Sự va chạm phải được giảm thiểu càng nhiều càng tốt.
Một số phương pháp được sử dụng để băm là:
Phương pháp phân chia
Nếu k
là một khóa và m
là kích thước của bảng băm, hàm băm h()
được tính như sau:
h(k) = k mod m
Ví dụ: Nếu kích thước của bảng băm là 10
và k = 112
sau đó là h(k) = 112
mod 10 = 2
. Giá trị của m
không được là quyền hạn của 2
. Điều này là do quyền hạn của 2
ở định dạng nhị phân là 10, 100, 1000,…
. Khi chúng ta tìm thấy k mod m
, chúng ta sẽ luôn nhận được p-bit bậc thấp hơn.
nếu m = 22, k = 17 thì h (k) = 17 mod 22 = 10001 mod 100 = 01 nếu m = 23, k = 17 thì h (k) = 17 mod 22 = 10001 mod 100 = 001 nếu m = 24, k = 17 thì h (k) = 17 mod 22 = 10001 mod 100 = 0001 nếu m = 2p thì h (k) = p bit thấp hơn của m
Phương pháp nhân
h(k) = ⌊m(kA mod 1)⌋
Ở đâu,
kA mod 1
đưa ra phần phân sốkA
,⌊ ⌋
cho giá trị sànA
là bất kỳ hằng số nào. Giá trị của sựA
nằm trong khoảng từ 0 đến 1. Nhưng, một lựa chọn tối ưu sẽ được≈ (√5-1)/2
đề xuất bởi Knuth.
Băm chung
Trong băm phổ quát, hàm băm được chọn ngẫu nhiên độc lập với các khóa.
Mở địa chỉ
Nhiều giá trị có thể được lưu trữ trong một vị trí duy nhất trong bảng băm thông thường.
Bằng cách sử dụng địa chỉ mở, mỗi vị trí được lấp đầy bằng một phím duy nhất hoặc bên trái NIL
. Tất cả các phần tử được lưu trữ trong chính bảng băm.
Unlike chaining, multiple elements cannot be fit into the same slot.
Open addressing is basically a collision resolving technique. Some of the methods used by open addressing are:
Linear Probing
In linear probing, collision is resolved by checking the next slot.h(k, i) = (h'(k) + i) mod m
where,
i = (0, 1,… .)
h'(k)
is a new hash function
If a collision occurs at h(k, 0)
, then h(k, 1)
is checked. In this way, the value of i
is incremented linearly.
The problem with linear probing is that a cluster of adjacent slots is filled. When inserting a new element, the entire cluster must be traversed. This adds to the time required to perform operations on the hash table.
Quadratic Probing
In quadratic probing, the spacing between the slots is increased (greater than one) by using the following relation.h(k, i) = (h'(k) + c1i + c2i2) mod m
where,
c1
và là các hằng số phụ dương,c2
i = (0, 1,… .)
Băm kép
Nếu xung đột xảy ra sau khi áp dụng một hàm băm h(k)
, thì một hàm băm khác sẽ được tính để tìm vị trí tiếp theo.h(k, i) = (h1(k) + ih2(k)) mod m
Ứng dụng bảng băm
Bảng băm được triển khai ở đâu
- tra cứu thời gian liên tục và chèn là bắt buộc
- ứng dụng mật mã
- dữ liệu lập chỉ mục là bắt buộc