Tôi biết rằng STL có API HashMap, nhưng tôi không thể tìm thấy bất kỳ tài liệu tốt và kỹ lưỡng nào với các ví dụ hay về điều này.
Bất kỳ ví dụ tốt sẽ được đánh giá cao.
Tôi biết rằng STL có API HashMap, nhưng tôi không thể tìm thấy bất kỳ tài liệu tốt và kỹ lưỡng nào với các ví dụ hay về điều này.
Bất kỳ ví dụ tốt sẽ được đánh giá cao.
Câu trả lời:
Thư viện tiêu chuẩn bao gồm các container ( std::map
và std::unordered_map
) bản đồ được sắp xếp theo thứ tự . Trong một bản đồ được sắp xếp, các phần tử được sắp xếp theo khóa, chèn và truy cập nằm trong O (log n) . Thông thường thư viện tiêu chuẩn bên trong sử dụng cây đen đỏ cho các bản đồ được đặt hàng. Nhưng đây chỉ là một chi tiết thực hiện. Trong một bản đồ không có thứ tự chèn và truy cập nằm trong O (1). Nó chỉ là một tên khác cho một hashtable.
Một ví dụ với (đã ra lệnh) std::map
:
#include <map>
#include <iostream>
#include <cassert>
int main(int argc, char **argv)
{
std::map<std::string, int> m;
m["hello"] = 23;
// check if key is present
if (m.find("world") != m.end())
std::cout << "map contains key world!\n";
// retrieve
std::cout << m["hello"] << '\n';
std::map<std::string, int>::iterator i = m.find("hello");
assert(i != m.end());
std::cout << "Key: " << i->first << " Value: " << i->second << '\n';
return 0;
}
Đầu ra:
23 Key: xin chào Giá trị: 23
Nếu bạn cần đặt hàng trong thùng chứa của mình và ổn với thời gian chạy O (log n) thì chỉ cần sử dụng std::map
.
Mặt khác, nếu bạn thực sự cần một bảng băm (O (1) chèn / truy cập), hãy kiểm tra std::unordered_map
, có std::map
API tương tự (ví dụ trong ví dụ trên bạn chỉ cần tìm kiếm và thay thế map
bằng unordered_map
).
Các unordered_map
thùng chứa đã được giới thiệu với C ++ 11 tiêu chuẩn sửa đổi. Do đó, tùy thuộc vào trình biên dịch của bạn, bạn phải kích hoạt các tính năng của C ++ 11 (ví dụ: khi sử dụng GCC 4.8, bạn phải thêm -std=c++11
vào CXXFLAGS).
Ngay cả trước khi phát hành C ++ 11, GCC đã hỗ trợ unordered_map
- trong không gian tên std::tr1
. Vì vậy, đối với các trình biên dịch GCC cũ, bạn có thể thử sử dụng nó như thế này:
#include <tr1/unordered_map>
std::tr1::unordered_map<std::string, int> m;
Nó cũng là một phần của boost, tức là bạn có thể sử dụng tiêu đề boost tương ứng để có tính di động tốt hơn.
hash_map
từ STL SGI trong một số hình thức này hay cách khác.
unordered_map
. Vì vậy, không có lý do để xem xét các tiêu chuẩn không hash_map
.
A hash_map
là phiên bản cũ hơn, không được chuẩn hóa cho mục đích tiêu chuẩn hóa được gọi là unordered_map
(ban đầu trong TR1 và được bao gồm trong tiêu chuẩn kể từ C ++ 11). Như tên của nó, nó khác với std::map
chủ yếu là không có thứ tự - ví dụ, nếu bạn lặp qua bản đồ từ begin()
đến end()
, bạn nhận được các mục theo thứ tự theo khóa 1 , nhưng nếu bạn lặp qua unordered_map
từ begin()
đến end()
, bạn sẽ nhận được các mục trong nhiều hay ít tùy ý.
An unordered_map
thường được dự kiến sẽ có độ phức tạp không đổi. Đó là, việc chèn, tra cứu, v.v., thường mất một khoảng thời gian cố định, bất kể có bao nhiêu mục trong bảng. Một std::map
sự phức tạp đó là logarit về số lượng vật phẩm được lưu trữ - có nghĩa là thời gian để chèn hoặc truy xuất một vật phẩm tăng lên, nhưng khá chậm , khi bản đồ phát triển lớn hơn. Ví dụ: nếu mất 1 micro giây để tra cứu một trong 1 triệu vật phẩm, thì bạn có thể mong đợi nó sẽ mất khoảng 2 micro giây để tra cứu một trong 2 triệu vật phẩm, 3 micro giây cho một trong 4 triệu vật phẩm, 4 micro giây cho một trong 8 triệu các mặt hàng, vv
Từ quan điểm thực tế, đó thực sự không phải là toàn bộ câu chuyện. Theo tự nhiên, một bảng băm đơn giản có kích thước cố định. Việc điều chỉnh nó theo các yêu cầu kích thước thay đổi cho một thùng chứa mục đích chung là không tầm thường. Do đó, các hoạt động (có khả năng) phát triển bảng (ví dụ: chèn) có khả năng tương đối chậm (nghĩa là hầu hết đều khá nhanh, nhưng theo định kỳ, một hoạt động sẽ chậm hơn nhiều). Tra cứu, không thể thay đổi kích thước của bảng, thường nhanh hơn nhiều. Do đó, hầu hết các bảng dựa trên hàm băm có xu hướng tốt nhất khi bạn thực hiện nhiều tra cứu so với số lần chèn. Đối với các tình huống bạn chèn nhiều dữ liệu, sau đó lặp qua bảng một lần để lấy kết quả (ví dụ: đếm số lượng từ duy nhất trong một tệp) có thể là mộtstd::map
sẽ nhanh như vậy, và thậm chí có thể nhanh hơn nữa (nhưng, một lần nữa, độ phức tạp tính toán là khác nhau, do đó cũng có thể phụ thuộc vào số lượng từ duy nhất trong tệp).
1std::less<T>
Theo mặc định , thứ tự được xác định bởi tham số mẫu thứ ba khi bạn tạo bản đồ .
rehash
. Khi bạn gọi rehash
, bạn chỉ định kích thước cho bảng. Kích thước đó sẽ được sử dụng trừ khi làm như vậy sẽ vượt quá hệ số tải tối đa được chỉ định cho bảng (trong trường hợp đó, kích thước sẽ được tăng tự động để giữ hệ số tải trong giới hạn).
Đây là một ví dụ đầy đủ và linh hoạt hơn mà không bỏ sót cần thiết bao gồm để tạo ra các lỗi biên dịch:
#include <iostream>
#include <unordered_map>
class Hashtable {
std::unordered_map<const void *, const void *> htmap;
public:
void put(const void *key, const void *value) {
htmap[key] = value;
}
const void *get(const void *key) {
return htmap[key];
}
};
int main() {
Hashtable ht;
ht.put("Bob", "Dylan");
int one = 1;
ht.put("one", &one);
std::cout << (char *)ht.get("Bob") << "; " << *(int *)ht.get("one");
}
Vẫn không đặc biệt hữu ích cho các khóa, trừ khi chúng được xác định trước là con trỏ, bởi vì giá trị khớp sẽ không làm được! (Tuy nhiên, vì tôi thường sử dụng chuỗi cho khóa, thay thế "chuỗi" cho "const void *" trong khai báo khóa sẽ giải quyết vấn đề này.)
void*
. Đối với người mới bắt đầu, không có lý do gì để bọc unordered_map
nó như là một phần của tiêu chuẩn và giảm khả năng duy trì mã. Tiếp theo, nếu nhấn mạnh vào gói nó, sử dụng templates
. Đó chính xác là những gì họ đang làm.
Bằng chứng std::unordered_map
sử dụng bản đồ băm trong GCC stdlibc ++ 6.4
Điều này đã được đề cập tại: https://stackoverflow.com/a/3578247/895245 nhưng trong câu trả lời sau: Cấu trúc dữ liệu nào nằm trong std :: map trong C ++? Tôi đã cung cấp thêm bằng chứng về việc triển khai GCC stdlibc ++ 6.4 bằng cách:
Dưới đây là bản xem trước của biểu đồ đặc tính hiệu suất được mô tả trong câu trả lời đó:
Cách sử dụng lớp tùy chỉnh và hàm băm với unordered_map
Câu trả lời này đóng đinh nó: C ++ unordered_map sử dụng loại lớp tùy chỉnh làm khóa
Trích: bình đẳng:
struct Key
{
std::string first;
std::string second;
int third;
bool operator==(const Key &other) const
{ return (first == other.first
&& second == other.second
&& third == other.third);
}
};
Hàm băm:
namespace std {
template <>
struct hash<Key>
{
std::size_t operator()(const Key& k) const
{
using std::size_t;
using std::hash;
using std::string;
// Compute individual hash values for first,
// second and third and combine them using XOR
// and bit shifting:
return ((hash<string>()(k.first)
^ (hash<string>()(k.second) << 1)) >> 1)
^ (hash<int>()(k.third) << 1);
}
};
}
Đối với những người trong chúng ta đang cố gắng tìm ra cách băm các lớp của chính mình trong khi vẫn sử dụng mẫu tiêu chuẩn, có một giải pháp đơn giản:
Trong lớp học của bạn, bạn cần xác định một quá tải toán tử đẳng thức ==
. Nếu bạn không biết cách thực hiện việc này, GeekforGeek có một hướng dẫn tuyệt vời https://www.geekforgeek.org/operator-overloading-c/
Trong không gian tên tiêu chuẩn, khai báo một cấu trúc mẫu được gọi là hàm băm với tên lớp của bạn là loại (xem bên dưới). Tôi đã tìm thấy một blogpost tuyệt vời cũng cho thấy một ví dụ về tính toán băm bằng XOR và bẻ khóa, nhưng nó nằm ngoài phạm vi của câu hỏi này, nhưng nó cũng bao gồm các hướng dẫn chi tiết về cách thực hiện bằng cách sử dụng hàm băm cũng như https://prateekvjoshi.com/ 2014/06/05 / using-hash-function-in-c-for-user-định-class /
namespace std {
template<>
struct hash<my_type> {
size_t operator()(const my_type& k) {
// Do your hash function here
...
}
};
}
std::map
hoặc std::unordered_map
giống như bạn thường làm và sử dụng my_type
làm khóa, thư viện chuẩn sẽ tự động sử dụng hàm băm mà bạn đã xác định trước (trong bước 2) để băm chìa khóa của bạn.#include <unordered_map>
int main() {
std::unordered_map<my_type, other_type> my_map;
}