Quand les listes ou matrices d'adjacence sont-elles le meilleur choix?

15

On m'a dit que nous utiliserions une liste si le graphique est clairsemé et une matrice si le graphique est dense . Pour moi, c'est juste une définition brute. Je ne vois pas grand-chose au-delà. Pouvez-vous préciser quand serait-ce le choix naturel à faire?

Merci d'avance!

user21312
la source
Ce n'est pas une définition, principalement parce qu'il n'y a pas de définition unique de "clairsemé" et "dense". En outre, il existe d'autres considérations, par exemple les aspects du graphique auxquels vous accédez à quelle fréquence.
Raphael
@Raphael Pouvez-vous entrer dans plus de détails sur les autres considérations?
user21312
1
@ user21312, une grande différence est l'itérabilité vs l'accès aux bords. Si vous avez souvent besoin d'itérer sur les bords, la liste adj pourrait être plus utile. Si vous devez souvent déterminer si un bord existe ou accéder à son poids (ou à d'autres informations), la matrice peut être meilleure.
ryan
Pour votre objectif, nous pourrions probablement négliger la définition de «clairsemé» et «dense». Modélisez simplement la complexité temporelle de l'opération matricielle que vous souhaitez utiliser pour chaque type de structure de données et voyez où se trouve le «point de rupture de la densité». Je pense que le deuxième lien de @ryan essaie de faire quelque chose de similaire
Apiwat Chantawibul

Réponses:

17

Tout d'abord, notez que clairsemé signifie que vous avez très peu d'arêtes, et dense signifie plusieurs arêtes, ou un graphique presque complet. Dans un graphique complet, vous avez arêtes, où n est le nombre de nœuds.n(n1)/2n

Maintenant, lorsque nous utilisons la représentation matricielle, nous allouons matrice pour stocker les informations de connectivité des nœuds, par exemple, M [ i ] [ j ] = 1 s'il y a un bord entre les nœuds i et j , sinon M [ i ] [ j ] = 0 . Mais si nous utilisons la liste d'adjacence, nous avons alors un tableau de nœuds et chaque nœud pointe vers sa liste d'adjacence contenant UNIQUEMENT ses nœuds voisins .n×nM[i][j]=1ijM[i][j]=0

Maintenant, si un graphe est clairsemé et que nous utilisons une représentation matricielle, la plupart des cellules matricielles restent inutilisées, ce qui entraîne un gaspillage de mémoire. Ainsi, nous n'utilisons généralement pas de représentation matricielle pour les graphiques clairsemés. Nous préférons la liste d'adjacence.

Mais si le graphe est dense alors le nombre d'arêtes est proche de (le complet) , ou de n 2 si le graphe est dirigé avec des auto-boucles. Il n'y a alors aucun avantage à utiliser la liste d'adjacence sur la matrice.n(n1)/2n2

En termes de complexité d'espace
Matrice d'adjacence: Liste d'adjacence: O ( n + m )n est le nombre de nœuds, m est le nombre d'arêtes.O(n2)
O(n+m)
nm

Lorsque le graphique est un arbre non orienté, alors
Matrice d'adjacence: Liste d'adjacence: O ( n + n ) est O ( n ) (mieux que n 2 )O(n2)
O(n+n)O(n)n2

Lorsque le graphique est dirigé, complet, avec auto-boucles puis
matrice d'adjacence: Liste d'adjacence: O ( n + n 2 ) est O ( n 2 ) (pas de différence)O(n2)
O(n+n2)O(n2)

Et enfin, lorsque vous implémentez en utilisant la matrice, vérifier s'il y a un bord entre deux nœuds prend fois, alors qu'avec une liste d'adjacence, cela peut prendre un temps linéaire en n .O(1)n

fade2black
la source
"alors qu'avec une liste de contiguïté, cela peut prendre un temps linéaire" - Étant donné que votre liste de contiguïté manque (probablement) de tout ordre naturel, pourquoi s'agit-il d'une liste au lieu d'un ensemble de hachage?
Kevin
1
@Kevin Ensuite, il serait appelé "hachage d'adjacence" au lieu de "liste". Aussi possible, pourquoi pas? Mais si vous faites simplement DFS ou BFS, ou une autre procédure qui analyse systématiquement tous les nœuds, quel est l'avantage d'utiliser le hachage sur la liste? Dans tous les cas, vous inspecteriez tous les nœuds adjacents.
fade2black
3
J'ajouterais que dans le cas non pondéré non orienté , pour un graphe presque complet, il pourrait être plus faisable de stocker son complément, c'est-à-dire un graphe clairsemé. Une matrice est donc utile quand environ la moitié des bords sont présents.
M. Winter
3

Pour répondre en fournissant une analogie simple. Si vous deviez stocker 6 onces d'eau, le feriez-vous (en général) avec un récipient de 5 gallons ou une tasse de 8 onces?

Maintenant, revenons à votre question. Si la majorité de votre matrice est vide, alors pourquoi l'utiliser? Inscrivez simplement chaque valeur à la place. Cependant, si votre liste est vraiment longue, pourquoi ne pas simplement utiliser une matrice pour la condenser?

Le raisonnement derrière la liste vs la matrice est vraiment aussi simple dans ce cas.

PS une liste est vraiment juste une matrice à une seule colonne !!! (en essayant de vous montrer à quel point cette décision / scénario est arbitraire)

Charles
la source
2

Considérons un graphique avec nœuds et E arêtes. En ignorant les termes d'ordre bas, une matrice de bits pour un graphe utilise N 2 bits quel que soit le nombre de fronts.NEN2

De combien de bits avez-vous réellement besoin?

En supposant que les arêtes sont indépendantes, le nombre de graphiques avec nœuds et E arêtes est ( N 2NE(N2E)log2(N2E)

EN22

E=N22log2(N2E)=N2+o(N2)EN2

log2(N2E)
=log2(N2)!E!(N2E)!
=2Elog2N+O(low order terms)

If you consider that log2N is the size of an integer which can represent a node index, the optimal representation is an array of 2E node ids, that is, an array of pairs of node indexes.

Having said that, a good measure of sparsity is the entropy, which is also the number of bits per edge of the optimal representation. If p=EN2 is the probability that an edge is present, the entropy is log2p(1p). For p12, the entropy is 2 (i.e. two bits per edge in the optimal representation), and the graph is dense. If the entropy is significantly greater than 2, and in particular if it's close to the size of a pointer, the graph is sparse.

Pseudonym
la source