セットがC++でどのように実装されているか知りたいのですが。 STL提供のコンテナーを使用せずに独自のセットコンテナーを実装する場合、このタスクを実行するにはどの方法が最適ですか?
STLセットがバイナリ検索ツリーの抽象的なデータ構造に基づいていることを理解しています。では、基礎となるデータ構造は何ですか?配列?
また、insert()
はセットでどのように機能しますか?セットは、要素がすでにその中に存在するかどうかをどのようにチェックしますか?
私はウィキペディアで、セットを実装する別の方法はハッシュテーブルを使用することだと読んだ。これはどのように機能しますか?
最初にNode
構造体を定義することにより、バイナリ検索ツリーを実装できます。
struct Node
{
void *nodeData;
Node *leftChild;
Node *rightChild;
}
次に、別のNode *rootNode;
を使用してツリーのルートを定義できます
Binary Search Tree のWikipediaエントリには、挿入メソッドを実装する方法のかなり良い例があるので、これもチェックすることをお勧めします。
重複に関しては、通常、セットでは許可されないため、仕様に応じて、その入力を破棄したり、例外をスローしたりすることができます。
KTCが言ったように、どのようにstd::set
実装はさまざまです。C++標準では、抽象データ型を指定するだけです。言い換えると、標準はコンテナの実装方法を指定せず、サポートするために必要な操作のみを指定します。ただし、STLのほとんどの実装では、私の知る限り、 red-black trees またはその他の種類のバランスのとれたバイナリ検索ツリーを使用します(たとえば、GNU libstdc ++は、赤黒ツリーを使用します)。 。
理論的には、セットをハッシュテーブルとして実装して、より速い漸近的なパフォーマンス(ルックアップと挿入の償却済みO(キー長)とO(log n)を比較)を得ることができますが、ユーザーが必要なタイプのハッシュ関数を指定する必要があります保存する(ハッシュテーブルの Wikipediaのエントリ を参照してください)。バイナリ検索ツリーの実装に関しては、配列を使用したくないでしょう-Raulが述べたように、ある種のNode
データ構造が必要でしょう。
_g++
_へのステップデバッグ__ 6.4 stdlibc ++ソース
Ubuntuの16.04のデフォルトの_g++-6
_パッケージまたは ソースからのGCC 6.4ビルド では、追加の設定なしでC++ライブラリにステップインできることをご存知ですか?
そうすることで、この実装では赤黒木が使用されていると簡単に結論付けられます。
_std::set
_を順番にたどることができるため、これは理にかなっています。これは、ハッシュマップが使用されている場合には効率的ではありません。
main.cpp
_#include <cassert>
#include <set>
int main() {
std::set<int> s;
s.insert(1);
s.insert(2);
assert(s.find(1) != s.end());
assert(s.find(2) != s.end());
assert(s.find(3) == s3.end());
}
_
コンパイルとデバッグ:
_g++ -g -std=c++11 -O0 -o main.out main.cpp
gdb -ex 'start' -q --args main.out
_
ここで、s.insert(1)
にステップインすると、すぐに_/usr/include/c++/6/bits/stl_set.h
_に到達します。
_487 #if __cplusplus >= 201103L
488 std::pair<iterator, bool>
489 insert(value_type&& __x)
490 {
491 std::pair<typename _Rep_type::iterator, bool> __p =
492 _M_t._M_insert_unique(std::move(__x));
493 return std::pair<iterator, bool>(__p.first, __p.second);
494 }
495 #endif
_
これは明らかに__M_t._M_insert_unique
_に転送するだけです。
したがって、vimでソースファイルを開き、__M_t
_の定義を見つけます。
_ typedef _Rb_tree<key_type, value_type, _Identity<value_type>,
key_compare, _Key_alloc_type> _Rep_type;
_Rep_type _M_t; // Red-black tree representing set.
_
したがって、__M_t
_は__Rep_type
_タイプであり、__Rep_type
_は__Rb_tree
_です。
OK、これで私には十分な証拠となりました。 __Rb_tree
_が黒赤ツリーであると思わない場合は、少し先に進んでアルゴリズムを読んでください。
_unordered_set
_はハッシュテーブルを使用します
同じ手順ですが、コードのset
を_unordered_set
_に置き換えます。
_std::unordered_set
_を順番にたどることができないため、これは理にかなっています。ハッシュマップの方が償却挿入時間が複雑になるため、標準ライブラリは赤黒木ではなくハッシュマップを選択しました。
insert
にステップインすると、_/usr/include/c++/6/bits/unordered_set.h
_につながります。
_415 std::pair<iterator, bool>
416 insert(value_type&& __x)
417 { return _M_h.insert(std::move(__x)); }
_
したがって、vim
でソースファイルを開き、__M_h
_を検索します。
_ typedef __uset_hashtable<_Value, _Hash, _Pred, _Alloc> _Hashtable;
_Hashtable _M_h;
_
だからハッシュテーブルです。
_std::map
_および_std::unordered_map
_
_std::set
_と_std:unordered_set
_の類似: C++のstd :: map内にはどのデータ構造がありますか?
パフォーマンス特性
タイミングをとることによって、使用されるデータ構造を推測することもできます。
グラフ生成手順とヒープとBSTの分析、および ヒープとバイナリ検索ツリー(BST)
私たちは明確に見ていきます:
std::set
_、対数挿入時間_std::unordered_set
_、より複雑なパターンのハッシュマップパターン:
ズームされたプロットでは、時間は基本的に一定で250nsに向かっていることがわかります。したがって、非常に小さいマップサイズを除いて、_std::map
_よりもはるかに高速です。
いくつかのストリップがはっきりと見え、アレイが2倍になるたびに、その傾きは小さくなります。
これは、各ビンでリンクリストのウォークが平均的に直線的に増加しているためだと思います。次に、配列が2倍になると、ビンの数が増えるため、歩く時間が短くなります。
STLセットがバイナリ検索ツリーの抽象的なデータ構造に基づいていることを理解しています。では、基礎となるデータ構造は何ですか?配列?
他の人が指摘したように、それは異なります。セットは通常、ツリー(赤黒ツリー、バランスツリーなど)として実装されますが、他の実装が存在する場合もあります。
また、セットに対してinsert()はどのように機能しますか?
それはあなたのセットの基礎となる実装に依存します。バイナリツリーとして実装されている場合、 Wikipedia には、insert()関数のサンプル再帰実装があります。ぜひチェックしてみてください。
セットは、要素がすでに存在するかどうかをどのようにチェックしますか?
ツリーとして実装されている場合は、ツリーを走査して各要素をチェックします。ただし、セットでは重複する要素を保存できません。要素の重複を許可するセットが必要な場合は、マルチセットが必要です。
私はウィキペディアで、セットを実装する別の方法はハッシュテーブルを使用することだと読んだ。これはどのように機能しますか?
ハッシュテーブルを使用してセットが実装されているhash_setを参照している可能性があります。要素を格納する場所を知るために、ハッシュ関数を提供する必要があります。この実装は、要素をすばやく検索できるようにする場合に最適です。ただし、要素を特定の順序で格納することが重要な場合は、ツリーの実装がより適切です。プレオーダー、インオーダー、ポストオーダーでトラバースできるためです。
特定のコンテナーがC++でどのように実装されるかは、完全に実装依存です。必要なのは、さまざまなメソッドの複雑さの要件、イテレータの要件など、標準で設定された要件を満たす結果を得るためだけです。
セットは通常赤黒木として実装されます。
チェックしたところ、libc++
とlibstdc++
はどちらもstd::set
に赤黒木を使用しています。
std::unordered_set
はlibc++
のハッシュテーブルを使用して実装されており、libstdc++
も同じだと思いますが、チェックしませんでした。
編集:どうやら私の言葉は十分ではありません。