STLにはHashMap APIが含まれていることは知っていますが、これに関する優れた例が記載された適切で完全なドキュメントは見つかりません。
良い例はありがたいです。
STLにはHashMap APIが含まれていることは知っていますが、これに関する優れた例が記載された適切で完全なドキュメントは見つかりません。
良い例はありがたいです。
回答:
標準ライブラリには、順序付きおよび順序なしのマップ(std::map
およびstd::unordered_map
)コンテナが含まれています。順序付けられたマップでは、要素はキーによってソートされ、挿入およびアクセスはO(log n)にあります。通常、標準ライブラリは、順序付けされたマップに内部で赤黒木を使用します。しかし、これは単なる実装の詳細です。順不同のマップでは、挿入とアクセスはO(1)で行われます。これはハッシュテーブルの単なる別名です。
(ordered)の例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;
}
出力:
23 キー:hello値:23
コンテナーでの注文が必要で、O(log n)ランタイムで問題がない場合は、を使用しますstd::map
。
そうでなければ、あなたは本当にハッシュテーブル(O(1)の挿入/アクセス)が必要な場合は、チェックアウトstd::unordered_map
に似ていた、std::map
(例えば上記の例では、あなただけの検索および置換する必要がAPIをmap
してunordered_map
)。
unordered_map
コンテナはで導入されたC ++ 11標準の改訂版。したがって、コンパイラーによっては、C ++ 11機能を有効にする必要があります(たとえば、GCC 4.8を使用する場合は-std=c++11
、CXXFLAGS に追加する必要があります)。
C ++ 11がリリースされる前でも、GCC unordered_map
は名前空間でサポートされていましたstd::tr1
。したがって、古いGCCコンパイラでは、次のように使用することができます。
#include <tr1/unordered_map>
std::tr1::unordered_map<std::string, int> m;
hash_map
unordered_map
です。したがって、非標準を考慮する理由はありませんhash_map
。
A hash_map
は、標準化の目的で呼ばれているものの古い非標準化バージョンですunordered_map
(元はTR1で、C ++ 11以降の標準に含まれています)。名前が示すように、std::map
主に順序付けされていないこととは異なります。たとえば、マップfromからbegin()
to を反復すると、end()
キー1の順に項目が取得されますが、unordered_map
from begin()
to を反復するend()
と、多かれ少なかれ任意の順序。
unordered_map
通常、一定の複雑さを持つことが期待されています。つまり、挿入、ルックアップなどは、テーブル内のアイテム数に関係なく、通常、基本的に一定の時間がかかります。Anがstd::map
格納されているアイテムの数に対数だ複雑さを持っている-非常にアイテムを挿入したり、検索する時間が増えることはなく、そのゆっくりとマップが大きくなるにつれて、。たとえば、100万個のアイテムの1つを検索するのに1マイクロ秒かかる場合、200万個のアイテムの1つを検索するのに約2マイクロ秒かかると予想できます。アイテムなど
実用的な観点からは、それだけではありません。本質的に、単純なハッシュテーブルのサイズは固定されています。汎用コンテナーの可変サイズ要件にそれを適合させることは、いくらか重要です。その結果、テーブルを(潜在的に)拡張する操作(挿入など)は、比較的低速になる可能性があります(つまり、ほとんどがかなり高速ですが、定期的に遅くなります)。テーブルのサイズを変更できないルックアップは、一般的にはるかに高速です。その結果、ほとんどのハッシュベースのテーブルは、挿入数と比較して多くのルックアップを行うときに最高の状態になる傾向があります。大量のデータを挿入する場合は、テーブルを1回繰り返して結果を取得します(たとえば、ファイル内の一意の単語の数を数える)。std::map
同じくらい高速で、おそらくさらに高速になります(ただし、計算の複雑さが異なるため、ファイル内の一意の単語の数にも依存します)。
1std::less<T>
デフォルトでは、マップの作成時に3番目のテンプレートパラメーターによって順序が定義されます。
rehash
。を呼び出すときrehash
に、テーブルのサイズを指定します。そのサイズは、それがテーブルに指定された最大負荷係数を超えない限り使用されます(この場合、サイズは、負荷係数を制限内に保つために自動的に増加します)。
コンパイルエラーを生成するために必要なインクルードを省略しない、より完全で柔軟な例を次に示します。
#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");
}
一致する値では機能しないため、ポインターとして事前定義されていない限り、キーには特に役立ちません。(ただし、通常はキーに文字列を使用するため、キーの宣言で「const void *」を「string」に置き換えると、この問題が解決します。
void*
ます。まずunordered_map
、標準の一部であり、コードの保守性が低下するため、をラップする理由はありません。次に、ラッピングを主張する場合は、を使用しますtemplates
。それがまさに彼らの目的です。
std::unordered_map
GCC stdlibc ++ 6.4でハッシュマップを使用する証拠
これはhttps://stackoverflow.com/a/3578247/895245で言及されていましたが、次の回答で:C ++のstd :: map内にはどのようなデータ構造がありますか?私はGCC stdlibc ++ 6.4実装のそのような証拠を次のように提供しました。
これは、その回答で説明されているパフォーマンス特性グラフのプレビューです。
カスタムクラスとハッシュ関数を使用する方法 unordered_map
この答えはそれを釘付けにします:キーとしてカスタムクラスタイプを使用するC ++ unordered_map
抜粋:平等:
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);
}
};
ハッシュ関数:
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);
}
};
}
標準テンプレートを使用しながら自分のクラスをハッシュする方法を理解しようとしている私たちのために、簡単な解決策があります:
クラスでは、等価演算子オーバーロードを定義する必要があります==
。これを行う方法がわからない場合は、GeeksforGeeksにすばらしいチュートリアルがあります。https: //www.geeksforgeeks.org/operator-overloading-c/
標準の名前空間で、型としてクラス名を使用して、hashというテンプレート構造体を宣言します(以下を参照)。XORとビットシフトを使用してハッシュを計算する例も示す優れたブログ投稿を見つけましたが、これはこの質問の範囲外ですが、ハッシュ関数を使用して実行する方法の詳細な手順も含まれていますhttps://prateekvjoshi.com/ 2014/06/05 /ユーザー定義クラス用のcでのハッシュ関数の使用/
namespace std {
template<>
struct hash<my_type> {
size_t operator()(const my_type& k) {
// Do your hash function here
...
}
};
}
std::map
か、std::unordered_map
通常のようにmy_type
キーとして使用するだけで、標準ライブラリは前に(手順2で)定義したハッシュ関数を自動的に使用してハッシュしますあなたの鍵。#include <unordered_map>
int main() {
std::unordered_map<my_type, other_type> my_map;
}