web-dev-qa-db-ja.com

C-Setデータ構造の実装方法

Cでセットデータ構造(一意の値のコレクション)を実装するトリッキーな方法はありますか?セット内のすべての要素は同じタイプであり、巨大なRAMメモリがあります。

私が知っているように、整数の場合は、値インデックス付き配列を使用して、本当に高速に実行できます。ただし、非常に一般的なSetデータ型が必要です。そして、セットに自分自身を含めることができればいいでしょう。

43
psihodelia

セットは通常、いくつかのさまざまな バイナリツリー として実装されます。 赤黒木 最悪の場合のパフォーマンスが良好です。

これらを使用して map を構築し、キー/値のルックアップを許可することもできます。

このアプローチでは、セットの要素とマップ内のキー値の並べ替えが必要です。

セットメンバーシップをCで明確に定義された型に制限する場合、バイナリツリーを使用してそれ自体を含む可能性のあるセットをどのように管理するかはわかりません...ただし、C++では十分簡単に​​実行できます。

5
andand

セット内の要素の最大数(基になるデータ型の基数)が十分に小さい場合、単純な古いビット配列(またはお気に入りの言語でそれらを呼び出すもの)の使用を検討することができます。

次に、単純なセットメンバーシップチェックを行います。要素nがセット内にある場合、ビットnは1です。 「通常の」メンバーを1から数え、セットにそれ自身が含まれる場合にのみビット0を1にすることもできます。

このアプローチでは、おそらくメンバーデータ型からビット配列内の位置に(および逆に)変換するために、何らかの他のデータ構造(または関数)が必要になりますが、基本的な集合演算(ユニオン、インターセクション、メンバーシップテスト、差分、挿入、取り外し、補完)は非常に簡単です。また、比較的小さなセットにのみ適しています。32ビット整数のセットには使用したくないと思います。

Cで汎用性を得る方法は_void *_であるため、とにかくポインターを使用することになり、異なるオブジェクトへのポインターは一意です。これは、ポインターを含むハッシュマップまたはバイナリツリーが必要であることを意味し、これはすべてのデータオブジェクトに対して機能します。

この欠点は、右辺値を個別に入力できないことです。値5を含むセットを持つことはできません。変数に5を割り当てる必要があります。これは、ランダム5とは一致しないことを意味します。_(void *) 5_として入力できます。実際には、これは小さな整数で機能する可能性が高いですが、整数が取得できる場合ポインターと競合するのに十分な大きさにすると、失敗する可能性は非常に小さくなります。

これは文字列値でも機能しません。 _char a[] = "Hello, World!"; char b[] = "Hello, World!";_を指定すると、ポインターのセットはabが異なることを検出します。値をハッシュしたいと思うかもしれませんが、ハッシュの衝突が心配な場合は、セットに文字列を保存し、strncmp()を実行して、保存された文字列とプローブ文字列を比較する必要があります。

(浮動小数点数にも同様の問題がありますが、最初は浮動小数点数をセットで表現しようとするのは悪い考えです。)

したがって、タグ付きの値、あらゆる種類のオブジェクト用のタグ、整数値用、文字列値用のタグが必要になるでしょう。複雑ですが、実行可能です。

2
David Thornley