Какой самый эффективный способ реализовать структуру данных графа?


14

Обычно я реализую графы в виде двусвязных списков, но в моем опыте это довольно неэффективно, поскольку мне нужно k указателей / ссылок для k соседей, поэтому для неориентированного графа в списках будет ~ 2k соседних ссылок, если моя математика верна. Есть ли лучший способ экономии места? Я знаю, что некоторые ссылки могут быть сделаны в единственном числе, если график направлен, но есть ли способ сделать это лучше?

Ответы:


12

Хорошо, если вам важна эффективность использования пространства, тогда лучше всего использовать сжатую структуру данных - но, конечно, это не очень эффективно для доступа или обновления .....

Если ваш граф имеет относительно небольшое количество узлов и достаточно плотный (скажем, существует не менее 5% всех возможных соединений), то вы можете обнаружить, что для создания матрицы смежности более эффективно использовать пространство , чем использовать списки ребер. Для этого потребуется всего один бит на каждое возможное (направленное) соединение и всего n * n бит, если у вас есть n узлов.

В противном случае, если вам нужно использовать соседние ссылки, вы не сможете легко справиться лучше, чем одна ссылка на ссылку, поскольку это минимальное информационное содержимое, которое вам нужно хранить. Если вам нужны обратные ссылки, вам понадобится вдвое больше ссылок.

Есть некоторые трюки, которые вы можете попробовать в дополнение к этому. Например, вы можете попробовать поделиться подмножествами ссылок (если A и B ссылаются на каждый из C, D, E, тогда сохраните список ссылок C, D, E только один раз .....). Однако это довольно быстро станет сложным, и я сомневаюсь, что в большинстве случаев это будет стоить усилий.

Еще одна хитрость - если у вашего графа разумное количество узлов, вы, безусловно, сэкономите место за счет индексации, например, используя 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];
    }    
}

(обратите внимание, что вы также можете выбрать тип индекса, в зависимости от объема данных, в виде байта или короткой строки или чего-то подобного, поскольку все индексы будут положительными. Я не считаю это микрооптимизацией, поскольку это тривиально)

Для ориентированного графа вы должны пойти по пути массива битов * n для хранения связности ... если только он не очень редок по сравнению с количеством узлов, где вы можете перейти к списку смежных индексов.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.