グラフデータ構造を実装する最もスペース効率の良い方法は何ですか?


14

私は通常、グラフを二重リンクリストとして実装しますが、これは私の経験ではかなりスペース効率が悪いです.k個の隣人にk個のポインタ/参照が必要なので、無向グラフの場合、数学が正しければリスト内に〜2k個の隣人リンクがあります。スペースを節約するより良い方法はありますか?グラフが方向付けられている場合、いくつかのリンクが特異になる可能性があることを知っていますが、これをもっとうまくやる方法はありますか?

回答:


12

空間効率が重要な場合は、圧縮されたデータ構造が最適です。しかし、もちろん、これはアクセスや更新にあまり効率的ではありません。

グラフのノード数が比較的少なく、密度がかなり高い場合(考えられるすべての接続の少なくとも5%が存在するとしましょう)、エッジリストを使用するよりも、隣接行列を作成する方がスペース効率が高いことがわかります。これには、可能な(有向)接続ごとに1ビットだけが必要であり、nノードがある場合は合計でn * nビットです。

それ以外の場合、隣接リンクを使用する必要がある場合、これは保存する必要のある最小限の情報コンテンツであるため、リンクごとに1つ以上の参照を簡単に行うことはできません。バックリンクが必要な場合は、2倍のリンクが必要になります。

この上で試すことができるいくつかのトリックがあります。たとえば、リンクのサブセットを共有してみてください(AとBがC、D、Eのそれぞれを参照している場合、リンクC、D、Eのリストを1回だけ保存します)。しかし、これは非常に迅速に複雑になり、ほとんどの場合、努力する価値があるとは思いません。

もう1つのトリック-グラフに適切な数のノードがあると仮定すると、インデックス作成によってスペースを確実に節約できます。たとえば、完全なポインタ/参照ではなく16ビットのノードインデックス番号を使用します。


すべてのリンクが無向の場合、エッジを低ノードから高ノードに保存するだけでスペースを半分に節約できます。
デュプリケータ

6

データの構造に依存します。

無向エッジの密なグラフの場合、三角行列を表すビット配列のリストに勝るものはありません。List<BitArray>例えば。論理的には、次のようになります。

 0123
0
11
211
3001
41010

そこから、ルートBitArrayのインデックスを使用して、ノードデータを格納するリストにインデックスを付けることができます。

たとえば、ノードのすべてのネイバーを取得するには、次のようにします。

// 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];
    }    
}

(すべてのインデックスが正になるので、データの量に応じて、バイトまたはushortまたはそれらの行に沿ったインデックスタイプを選択することもできます。これは些細なことなので、マイクロ最適化とは見なしません)

有向グラフの場合、ノードの数と比較して非常にまばらである場合を除き、インデックスの隣接リストに移動できる場合を除き、接続を格納するためにビット配列のルートに移動します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.