Frage C - Wie implementiert man die Datenstruktur?


Gibt es eine knifflige Möglichkeit, eine festgelegte Datenstruktur (eine Sammlung eindeutiger Werte) in C zu implementieren? Alle Elemente in einem Set sind vom selben Typ und es gibt einen riesigen RAM-Speicher.

Wie ich weiß, kann man es für ganzzahlige Zahlen sehr schnell machen, indem man wert-indexierte Arrays verwendet. Aber ich hätte gerne einen sehr allgemeinen Set-Datentyp. Und es wäre schön, wenn ein Set sich selbst enthalten könnte.


34
2018-04-13 15:15


Ursprung


Antworten:


Es gibt mehrere Möglichkeiten der Implementierung Set- (und Map-) Funktionalität, zum Beispiel:

  • Baumbasierter Ansatz (geordnete Traversierung)
  • Hash-basierter Ansatz (ungeordnetes Traversal)

Schon seit Sie haben wert-indexierte Arrays erwähntVersuchen wir den Hash-basierten Ansatz, der baut natürlich auf der wertindexierten Array-Technik auf.

Vorsicht vor dem Vorteile und Nachteile von Hash-basierten vs. Baum-basierten Ansätzen.

Sie können ein Hash-Satz (ein Sonderfall von Hash-Tabellen) von Zeigern auf waschbar  PODs, mit Verkettung, intern dargestellt als eine feste Größe von Eimern von Hashabeln, woher:

  • alle Hashabeln in einem Bucket haben den gleichen Hash-Wert
  • Ein Bucket kann als ein implementiert werden dynamisches Array oder verknüpfte Liste von Hashes
  • ein waschbarist es Hash-Wert wird verwendet, um in das Array von Buckets zu indizieren (Hash-Wert-indiziertes Array)
  • einer oder mehrere der Hashabeln in dem Hash-Satz enthalten könnte (ein Zeiger auf) einen anderen Hash-Satz oder sogar zu dem Hash-Satz selbst (d. h. Selbsteinbeziehung ist möglich)

Mit großen Mengen an Speicher zur Verfügung, können Sie Ihr Array von Eimern großzügig bemessen und in Kombination mit einer guten Hash-Methode drastisch reduzieren die Wahrscheinlichkeit von Kollision, praktisch konstante Leistung zu erreichen.

Sie müssten implementieren:

  • das Hash-Funktion für den Typ, der gehashed wird
  • eine Gleichheitsfunktion für den Typ, mit dem geprüft wird, ob zwei Hashables gleich sind oder nicht
  • der Hash-Satz contains/insert/remove Funktionalität.

Sie können auch verwenden offene Adressierung als Alternative zur Wartung und Verwaltung von Eimern.


42
2018-04-13 15:21



Sätze werden normalerweise als eine Vielzahl von a implementiert Binärbaum. Rote schwarze Bäume gute Leistung im schlechtesten Fall.

Diese können auch verwendet werden, um ein Karte um Schlüssel / Wert-Lookups zu ermöglichen.

Dieser Ansatz erfordert eine gewisse Reihenfolge der Elemente der Menge und der Schlüsselwerte in einer Map.

Ich bin mir nicht sicher, wie Sie eine Menge verwalten würden, die sich möglicherweise unter Verwendung binärer Bäume enthalten könnte, wenn Sie die Satzmitgliedschaft auf gut definierte Typen in C beschränken ... ein Vergleich zwischen solchen Konstrukten könnte problematisch sein. Sie könnten es jedoch leicht genug in C ++ tun.


5
2018-04-13 15:23



Wenn die maximale Anzahl der Elemente im Satz (die Kardinalität des zugrunde liegenden Datentyps) klein genug ist, sollten Sie ein einfaches altes Array von Bits (oder wie immer Sie es in Ihrer bevorzugten Sprache nennen) verwenden.

Dann haben Sie eine einfache Mitgliedschaftsprüfung: Bit n ist 1, wenn Element n in der Menge ist. Sie können sogar "normale" Mitglieder von 1 zählen und nur Bit 0 gleich 1 machen, wenn das Set sich selbst enthält.

Dieser Ansatz erfordert wahrscheinlich eine andere Art von Datenstruktur (oder Funktion), um vom Elementdatentyp in die Position im Bitarray (und zurück) zu übersetzen, aber er führt grundlegende Mengenoperationen aus (Vereinigung, Schnittpunkt, Mitgliedschaftstest, Differenz, Einsetzen, Entfernen, Zwang) sehr sehr einfach. Und es ist nur für relativ kleine Sätze geeignet, Sie würden es nicht für Sätze von 32-Bit-Ganzzahlen verwenden wollen, die ich nicht annehme.


2
2018-04-13 15:42



Der Weg, um Generizität in C zu bekommen, ist durch void *, also werden Sie sowieso Zeiger verwenden, und Zeiger auf verschiedene Objekte sind eindeutig. Dies bedeutet, dass Sie eine Hash-Map oder einen Binärbaum benötigen, der Zeiger enthält. Dies funktioniert für alle Datenobjekte.

Der Nachteil davon ist, dass Sie Rvalues ​​nicht unabhängig eingeben können. Sie können kein Set mit dem Wert 5 haben. Sie müssen einer Variablen 5 zuweisen, was bedeutet, dass sie keiner zufälligen 5 entspricht. Sie könnten sie als eingeben (void *) 5Für praktische Zwecke funktioniert dies wahrscheinlich mit kleinen ganzen Zahlen, aber wenn Ihre ganzen Zahlen groß genug sind, um mit Zeigern zu konkurrieren, hat dies eine sehr geringe Wahrscheinlichkeit, dass sie versagen.

Dies funktioniert auch nicht mit String-Werten. Gegeben char a[] = "Hello, World!"; char b[] = "Hello, World!";würde eine Reihe von Zeigern finden a und b anders sein. Sie würden wahrscheinlich die Werte hashen, aber wenn Sie sich Gedanken über Hash-Kollisionen machen, sollten Sie die Zeichenfolge in der Menge speichern und a strncmp() um die gespeicherte Zeichenfolge mit der Prüfzeichenfolge zu vergleichen.

(Es gibt ähnliche Probleme mit Gleitkommazahlen, aber der Versuch, Gleitkommazahlen in Mengen darzustellen, ist von vornherein eine schlechte Idee.)

Daher möchten Sie wahrscheinlich einen Tag-Wert, ein Tag für jede Art von Objekt, einen für Integer-Wert und einen für String-Wert und möglicherweise mehr für verschiedene Arten von Werten. Es ist kompliziert, aber machbar.


2
2018-04-13 15:48