Was ist die platzsparendste Methode zum Implementieren einer Diagrammdatenstruktur?

14

Normalerweise implementiere ich Graphen als doppelt verknüpfte Listen, aber dies ist meiner Erfahrung nach recht ineffizient, da ich k Zeiger / Referenzen für k Nachbarn benötige. Für einen ungerichteten Graphen hätte ich also ~ 2k Nachbarlinks in den Listen, wenn meine Mathematik stimmt. Gibt es eine bessere Möglichkeit, Platz zu sparen? Ich weiß, dass einige der Links singulär gemacht werden können, wenn das Diagramm gerichtet ist, aber gibt es eine Möglichkeit, dies besser zu machen?

Weltingenieur
quelle

Antworten:

12

Nun, wenn Sie sich nur um die Speichereffizienz kümmern, ist eine komprimierte Datenstruktur am besten - aber dies ist natürlich nicht sehr effizient für den Zugriff oder die Aktualisierung .....

Wenn Ihr Diagramm eine relativ kleine Anzahl von Knoten hat und ziemlich dicht ist (sagen wir, dass mindestens 5% aller möglichen Verbindungen vorhanden sind), ist es möglicherweise platzsparender, eine Adjazenzmatrix zu erstellen, als Kantenlisten zu verwenden. Dies würde nur ein Bit pro möglicher (gerichteter) Verbindung erfordern und insgesamt n * n Bits, wenn Sie n Knoten haben.

Andernfalls, wenn Sie Nachbarlinks verwenden müssen, können Sie nicht einfach mehr als eine Referenz pro Link erstellen, da dies der minimale Informationsinhalt ist, den Sie speichern müssen. Wenn Sie Backlinks benötigen, benötigen Sie doppelt so viele Links.

Darüber hinaus gibt es einige Tricks, die Sie ausprobieren können. Sie können beispielsweise versuchen, Teilmengen von Links gemeinsam zu nutzen (wenn sich A und B auf C, D, E beziehen, wird die Liste der Links C, D, E nur einmal gespeichert .....). Dies wird jedoch ziemlich schnell komplex und ich bezweifle, dass es sich in den meisten Fällen lohnen wird.

Ein weiterer Trick: Vorausgesetzt, Ihr Diagramm verfügt über eine angemessene Anzahl von Knoten, können Sie durch die Indizierung Platz sparen - z. B. mit einer 16-Bit-Knotenindexnummer anstelle eines vollständigen Zeigers / einer vollständigen Referenz.

mikera
quelle
Wenn alle Verbindungen ungerichtet sind, kann man die Hälfte des Platzes einsparen, indem nur die Kante vom unteren Knoten zum oberen Knoten gespeichert wird.
Deduplicator
6

Es wird von der Struktur Ihrer Daten abhängen.

Bei einem dichten Graphen mit ungerichteten Kanten können Sie eine Liste von Bit-Arrays, die eine Dreiecksmatrix darstellen, nicht wirklich übertreffen. A List<BitArray>zum Beispiel. Logischerweise würde es so aussehen:

 0123
0
11
211
3001
41010

Von dort aus können Sie den Index des Root-BitArray verwenden, um eine Liste zu indizieren, in der Ihre Knotendaten gespeichert sind.

Das Abrufen aller Nachbarn eines Knotens würde beispielsweise folgendermaßen aussehen:

// C#
List<Node> Nodes = /* populated elsewhere */
List<BitArray> bits = /* populated elsewhere */
public static IEnumerable<Node> GetNeighbours(int x)    
{
    for (int i = 0; i < bits[idx].Count; i++)
    {
        if (this.bits[idx][i])
            yield return this.Nodes[i];
    }

    for (int i = 0; i < this.Nodes.Count; i++)
    {
        if (idx < this.bits[i].Count && this.bits[i][idx])
            yield return this.Nodes[i];
    }    
}

(Beachten Sie, dass Sie den Indextyp auch abhängig von der Datenmenge als Byte, Kurzform oder Ähnliches auswählen können, da alle Indizes positiv sind. Ich halte dies nicht für eine Mikrooptimierung, da dies trivial ist.)

Für einen gerichteten Graphen würden Sie die Route eines * n-Arrays von Bits gehen, um die Konnektivität zu speichern.

Steven Evers
quelle