- vừa được xem lúc

Data structures: Hash table

0 0 25

Người đăng: Hiếu Đỗ

Theo Viblo Asia

Giới thiệu

Hash Table là một cấu trúc dữ liệu vô cùng quan trọng có ở hầu hết các ngôn ngữ, là một tronng nhữg nền tảng của Cấu trúc dữ liệu và thuật toán.
Hash table là một cấu trúc dữ liệu lưu dữ liệu theo một cặp key - value, nó sử dụng một hàm Hash để tính toán vị trí lưu dữ liệu, nơi đó sẽ lưu một bucket để ta có thể tìm dữ liệu.

Giải thích

Có thể giải thích tương tự như việc cất sách và tìm sách trong thư viện, mỗi quyển sách sẽ khi được nhận về sẽ được đánh giá vào một thể loại riêng (tương tự như hàm Hash). Sau khi biết được thể loại của sách, chúng ta sẽ để nó vào khu vực của loại sách đó. Do đó mỗi một vị trí để sách sẽ có nhiều quyển sách cùng loại (tương tự như Hash Collisions chúng ta sẽ tìm hiểu ở bước tiếp theo)
Khi đó, mỗi khi có một người tìm một quyển sách, nhân viên thư viện có thể tra cứu và biết được quyển sách đó thuộc thể loại gì ở khu vực nào và đến khu vực đó để tìm sách

Hash function

Là một giải thuật nhằm sinh ra một giá trị với mỗi khối dữ liệu (có thể là String hay một đối tượng trong lập trình hướng đối tượng). Với mỗi một đầu vào giống nhau, hàm băm phải luôn sinh ra cùng một giá trị.
Hash function dễ dàng có thể implement nhất là dựa trên mã ACSII của kí tự
Giả sử ta có bộ nhớ cho Hash là 1000 Ví dụ như dựa trên công thức: Giả sử ta

(s.charAt(0) * n + s.charAt(1) * (n-1) + ... + s.charAt(n-1)) % 1000

Thì với chuỗi "ABC" khi hash ta sẽ có

"ABC" = ('A' * 3 + 'B' * 2 + 'C') % 1000 = 394

Như vậy ABC sẽ có địa chỉ 394
Như vậy khi tìm dữ liệu với key là "ABC", ta sẽ tìm value ở địa chỉ 394

Hash Collision

Như ví dụ trên về Hash Function, chúng ta có thể thấy sẽ có những trường hợp mà khác đầu vào nhưng hàm Hash cũng sẽ trả ra cùng giá trị. Vấn đề xảy ra khi hàm Hash không đủ tôt, và cũng sẽ không có thuật toán nào hoàn hảo nếu dữ liệu đầu vào quá lớn, do đó chúng ta cần giải quyết vấn đề Hash Collision này.
Để giải quyết vấn đề này, chúng ta có thể dùng hàm Hash để hash ra địa chỉ, rồi lưu cả cặp key-value vào địa chỉ với một mảng hoặc một Linked-List. Rồi khi tìm kiếm, chúng ta sẽ dùng hàm Hash để lấy địa chỉ, sau đó tìm kiếm giá trị trong mảng hoặc linked-list đó với key thật. Tương tự như việc đi đến khu vực của thể loại sách ở thư viện rồi chúng ta tìm sách.

Các hàm cơ bản của Hash Table

Hash Table có hai hàm cơ bản đó là Set và Get

Set

 myHash.set("ABC", 12345); // O(1) 

Hàm set nhận vào một cặp Key-Value

Get

 myHash.get("ABC"); // O(1) average và O(n) worst

Hàm get sẽ nhận vào một key và trả về một giá trị.

Implement

Sau đây mình sẽ implement một Hash table để các bạn có thể hiểu thêm và dễ hình dung:

class MyHashTable { final int size; late final List<List<KeyValue>?> _data; MyHashTable(this.size) { _data = List.filled(size, null); } int _hash(dynamic key) { var hash = 0; key.toString().runes.forEach((element) { hash += element; }); return hash % size; } void set(key, value) { final address = _hash(key); if (_data[address] == null) { _data[address] = []; } _data[address]!.add(KeyValue(key.toString(), value)); } dynamic get(key) { final address = _hash(key); final bucket = _data[address]; try { return bucket?.firstWhere((element) => element.hasKey(key)).value; } catch (error) { return null; } } List<dynamic> keys() { return _data .toList() .expand( (element) => ((element ?? <KeyValue>[]).map((e) => e.key).toList())) .toList(); } @override String toString() { return _data.toString(); }
} class KeyValue { final String key; final dynamic value; KeyValue(this.key, this.value); bool hasKey(dynamic key) => this.key == key.toString(); @override String toString() { return "{key:$key,value:$value}"; }
}

Qua ví dụ trên, các bạn có thể dễ hình dung hơn về việc Set hay Get data của Hash Table, cũng như cách giải quyết Collision của Hash.
Các bạn cũng có thể hiểu vì sao trong trường hợp bình thường, việc Get data của Hash table là O(1) nhưng trong trường hợp số lượng dữ liệu đầu vào quá lớn so với kích thước địa chỉ của hàm Hash thì việc get Data lại là O(n)

Tổng kết

Ưu điểm

  • Fast lookups* (Cần cách giải quyết Collision tốt)
  • Fast inserts
  • Flexible keys

Nhược điểm

  • Không có thự tự
  • Lấy phần tử chậm nếu giải quyết Collision không tốt

So sánh với Array

Array mạnh hơn HashTable trong trường hợp cần lưu dữ liệu cùng loại, có tính lặp lại, ít insert, delete, thường xuyên truy cập phần tử.
HashTable mạnh hơn trong trường hợp thường xuyên insert, delete, không cần dữ liệu có thứ tự.

Hi vọng qua bài viết này, chúng ta sẽ hiểu thêm về Hash Table và cách sử dụng của nó.

Goodluck

Bình luận

Bài viết tương tự

- vừa được xem lúc

Thuật toán quay lui (Backtracking)

Quay lui là một kĩ thuật thiết kế giải thuật dựa trên đệ quy. Ý tưởng của quay lui là tìm lời giải từng bước, mỗi bước chọn một trong số các lựa chọn khả dĩ và đệ quy.

0 0 38

- vừa được xem lúc

Các thuật toán cơ bản trong AI - Phân biệt Best First Search và Uniform Cost Search (UCS)

Nếu bạn từng đọc các thuật toán trong AI (Artificial Intelligence - Trí tuệ nhân tạo), rất có thể bạn từng nghe qua về các thuật toán tìm kiếm cơ bản: UCS (thuộc chiến lược tìm kiếm mù) và Best First Search (thuộc chiến lược tìm kiếm kinh nghiệm). Khác nhau rõ từ khâu phân loại rồi, thế nhưng hai th

0 0 152

- vừa được xem lúc

Sử dụng vector trong lập trình C++ - giải bài toán lập trình muôn thủa

Chào buổi tối mọi người, hôm nay lang thang trên mạng bắt gặp bài toán quen thuộc một thời của quãng đường sinh viên IT. Đấy chính là câu số 1 trong đề thi dưới đây:.

0 0 35

- vừa được xem lúc

MÔ PHỎNG THUẬT TOÁN VƯƠNG HẠO TRONG PROLOG

. 1. Các luật suy diễn trong thuật toán Vương Hạo. Luật 1: Chuyển vế các giả thuyết và kết luận ở dạng phủ định. Ví dụ: p v q, !(r ^ s), !q, p v r -> s, !p <=> p v q, p v r, p -> s, r ^ s, q.

0 0 76

- vừa được xem lúc

A* Search Algorithm

What is A* Search Algorithm. How it works. . Explanation.

0 0 42

- vừa được xem lúc

Python: Jump Search

Search là một từ khóa khá là quen thuộc đối với chúng ta. Hiểu theo đúng nghĩa đen của nó chính là "Tìm kiếm".

0 0 35