Bir grafiği bellekte saklamanın üç yolu, avantajları ve dezavantajları


90

Bir grafiği hafızaya kaydetmenin üç yolu vardır:

  1. Nesne olarak düğümler ve işaretçiler olarak kenarlar
  2. Numaralı düğüm x ve düğüm y arasındaki tüm kenar ağırlıklarını içeren bir matris
  3. Numaralı düğümler arasındaki kenarların listesi

Üçünü de nasıl yazacağımı biliyorum, ancak her birinin avantajlarını ve dezavantajlarını düşündüğümden emin değilim.

Bir grafiği belleğe kaydetmenin bu yollarının her birinin avantajları ve dezavantajları nelerdir?


3
Matrisi yalnızca grafik birbirine çok bağlıysa veya çok küçükse dikkate alırdım. Seyrek bağlı grafikler için, nesne / işaretçi veya kenarlar listesi yaklaşımlarının her ikisi de çok daha iyi bellek kullanımı sağlar. Depolamanın yanı sıra neyi gözden kaçırdığımı merak ediyorum. ;)
sarnold

2
Zaman karmaşıklığı bakımından da farklılık gösterirler, matris O (1) ve diğer temsiller aradığınız şeye bağlı olarak büyük ölçüde değişebilir.
msw

1
Bir süre önce, bir işaretçi listesi üzerinde bir matris olarak bir grafiği uygulamanın donanım avantajlarını açıklayan bir makale okuduğumu hatırlıyorum. Bitişik bir bellek bloğu ile uğraştığınız için, herhangi bir zamanda, çalışma kümenizin çoğu L2 önbelleğinde olabilir. Öte yandan, düğümlerin / işaretçilerin bir listesi bellek yoluyla atılabilir ve muhtemelen önbelleğe çarpmayan bir getirme gerektirebilir. Katılıyorum emin değilim ama bu ilginç bir düşünce.
nerraga

1
@Dean J: "Nesneler olarak düğümler ve işaretçilerin temsili olarak kenarlar" hakkında bir soru. İşaretçileri nesnede saklamak için hangi veri yapısını kullanıyorsunuz? Bu bir liste mi?
Timofey

4
Yaygın isimler şunlardır: (1) bitişik listeye eşdeğer , (2) bitişik matris , (3) kenar listesi .
Evgeni Sergeev

Yanıtlar:


51

Bunları analiz etmenin bir yolu bellek ve zaman karmaşıklığı (grafiğe nasıl erişmek istediğinize bağlıdır) açısından analiz etmektir.

Düğümleri birbirine işaret eden nesneler olarak saklama

  • Bu yaklaşım için bellek karmaşıklığı O (n) 'dir çünkü düğümleriniz kadar çok nesneye sahipsiniz. Her düğüm nesnesi düğüme kadar işaretçiler içerebileceğinden, gerekli işaretçiler (düğümlere) sayısı O (n ^ 2) 'ye kadardır.
  • Bu veri yapısının zaman karmaşıklığı, herhangi bir düğüme erişmek için O (n) 'dur.

Kenar ağırlıklarından oluşan bir matris saklama

  • Bu, matris için O (n ^ 2) 'nin bir hafıza karmaşıklığı olacaktır.
  • Bu veri yapısının avantajı, herhangi bir düğüme erişmek için zaman karmaşıklığının O (1) olmasıdır.

Grafikte hangi algoritmayı çalıştırdığınıza ve kaç düğüm olduğuna bağlı olarak, uygun bir temsil seçmeniz gerekir.


3
Nesne / işaretçi modelindeki aramalar için zaman karmaşıklığının, düğümleri ayrı bir dizide de saklarsanız, yalnızca O (n) olduğuna inanıyorum. Aksi takdirde, istenen düğümü arayarak grafiği geçmeniz gerekir, değil mi? Rasgele bir grafikte her düğümü (ancak her kenarı değil) geçmek O (n) 'de yapılamaz, değil mi?
Barry Fruitman

@BarryFruitman Doğru olduğunuzdan oldukça eminim. BFS O (V + E) 'dir. Ayrıca, diğer düğümlere bağlı olmayan bir düğümü arıyorsanız, onu asla bulamazsınız.
WilderField

10

Dikkate alınması gereken birkaç nokta daha:

  1. Matris modeli, ağırlıkları matriste depolayarak ağırlıklı kenarlı grafiklere daha kolay uyum sağlar. Nesne / işaretçi modelinin, işaretçi dizisiyle senkronizasyon gerektiren paralel bir dizide kenar ağırlıklarını depolaması gerekir.

  2. Nesne / işaretçi modeli, yönlendirilmiş grafiklerle yönlendirilmemiş grafiklerden daha iyi çalışır çünkü işaretçilerin eşzamanlı olmayan hale gelebilecek çiftler halinde tutulması gerekir.


1
İşaretçilerin yönsüz grafiklerle çiftler halinde tutulması gerektiğini söylüyorsunuz, değil mi? Yönlendirilmişse, belirli bir tepe noktasının bitişiklik listesine bir köşe eklemeniz yeterlidir, ancak yönlendirilmemişse, her iki köşenin bitişiklik listesine bir tane eklemeniz gerekir.
FrostyStraw

@FrostyStraw Evet, aynen öyle.
Barry Fruitman

8

Nesneler ve işaretçiler yöntemi, bazılarının da belirttiği gibi, arama zorluğundan muzdariptir, ancak birçok ekstra yapının olduğu ikili arama ağaçları oluşturmak gibi şeyler yapmak için oldukça doğaldır.

Ben şahsen bitişik matrisleri seviyorum çünkü cebirsel grafik teorisindeki araçları kullanarak her türlü problemi çok daha kolay hale getiriyorlar. (Bitişik matrisin k'inci kuvveti, örneğin, tepe noktasından j köşesine k uzunluğundaki yolların sayısını verir. <= K uzunluğundaki yolların sayısını elde etmek için k'inci kuvvetini almadan önce bir kimlik matrisi ekleyin. Bir sıra alın Laplacian'ın n-1 minör, yayılan ağaçların sayısını elde etmek için ... vb.)

Ama herkes bitişik matrislerin hafıza açısından pahalı olduğunu söylüyor! Yalnızca yarısı doğrudur: Grafiğinizin az kenarı olduğunda seyrek matrisler kullanarak bunu aşabilirsiniz. Seyrek matris veri yapıları, tam olarak bir bitişiklik listesi tutma işini yapar, ancak yine de size her iki dünyanın da en iyisini sunan tüm standart matris işlemleri yelpazesine sahiptir.


7

İlk örneğinizin biraz belirsiz olduğunu düşünüyorum - nesneler olarak düğümler ve işaretçiler olarak kenarlar. Bunları yalnızca bazı kök düğüme bir işaretçi depolayarak takip edebilirsiniz, bu durumda belirli bir düğüme erişim verimsiz olabilir (örneğin 4. düğümü istediğinizi söyleyin - düğüm nesnesi sağlanmadıysa, onu aramanız gerekebilir) . Bu durumda, grafiğin kök düğümden ulaşılamayan kısımlarını da kaybedersiniz. Sanırım f64 Rainbow, belirli bir düğüme erişmek için zaman karmaşıklığının O (n) olduğunu söylediğinde gökkuşağı varsayıyor.

Aksi takdirde, her bir düğüme yönelik işaretçilerle dolu bir dizi (veya karma haritası) tutabilirsiniz. Bu, belirli bir düğüme O (1) erişimine izin verir, ancak bellek kullanımını biraz artırır. Eğer n düğüm sayısı ve e kenar sayısı ise, bu yaklaşımın uzay karmaşıklığı O (n + e) ​​olacaktır.

Matris yaklaşımı için uzay karmaşıklığı, O (n ^ 2) çizgileri boyunca olacaktır (kenarların tek yönlü olduğu varsayılarak). Grafiğiniz seyrekse, matrisinizde çok sayıda boş hücre olacaktır. Ancak grafiğiniz tamamen bağlantılıysa (e = n ^ 2), bu ilk yaklaşımla olumlu bir şekilde karşılaştırılır. RG'nin dediği gibi, matrisi tek bir bellek parçası olarak ayırırsanız, bu yaklaşımda daha az önbellek eksikliğiniz olabilir, bu da grafiğin etrafındaki birçok kenarı daha hızlı takip edebilir.

Üçüncü yaklaşım, çoğu durum için muhtemelen en verimli alan - O (e) - ancak belirli bir düğümün tüm kenarlarını bulmayı bir O (e) işi haline getirecektir. Bunun çok yararlı olacağı bir durum düşünemiyorum.


Kenar listesi, Kruskal'ın algoritması için doğaldır ("her kenar için, birleşim bul'da bir arama yapın"). Ayrıca, Skiena (2. baskı, sayfa 157), Combinatorica ( birçok algoritmanın genel amaçlı bir kütüphanesi olan) kütüphanesinde grafikler için temel veri yapısı olarak kenar listelerinden bahseder . Bunun nedenlerinden birinin, Combinatorica'nın yaşadığı ortam olan Mathematica'nın hesaplama modelinin dayattığı kısıtlamalardan bahsetmektedir.
Evgeni Sergeev


4

Başka bir seçenek daha var: nesneler olarak düğümler, nesneler olarak kenarlar da, her kenar aynı anda iki çift bağlantılı listede yer alıyor: aynı düğümden çıkan tüm kenarların listesi ve aynı düğüme giden tüm kenarların listesi .

struct Node {
    ... node payload ...
    Edge *first_in;    // All incoming edges
    Edge *first_out;   // All outgoing edges
};

struct Edge {
    ... edge payload ...
    Node *from, *to;
    Edge *prev_in_from, *next_in_from; // dlist of same "from"
    Edge *prev_in_to, *next_in_to;     // dlist of same "to"
};

Bellek ek yükü büyüktür (düğüm başına 2 işaretçi ve kenar başına 6 işaretçi) ancak

  • O (1) düğüm ekleme
  • O (1) kenar ekleme ("nereden" ve "den" düğümlere işaretçiler verilir)
  • O (1) kenar silme (işaretçi verildiğinde)
  • O (derece (n)) düğüm silme (işaretçi verildiğinde)
  • O (deg (n)) bir düğümün komşularını bulma

Yapı aynı zamanda oldukça genel bir grafiği de temsil edebilir: döngülerle yönlendirilmiş çoklu grafi (yani, aynı iki düğüm arasında birden çok farklı döngü dahil olmak üzere birden fazla farklı kenarınız olabilir - x'ten x'e giden kenarlar).

Bu yaklaşımın daha ayrıntılı bir açıklaması burada mevcuttur .


3

Tamam, eğer kenarların ağırlıkları yoksa, matris bir ikili dizi olabilir ve bu durumda ikili operatörler kullanmak işlerin gerçekten çok hızlı ilerlemesini sağlayabilir.

Grafik seyrekse, nesne / işaretçi yöntemi çok daha verimli görünür. Nesneyi / işaretçileri özellikle onları tek bir bellek yığınına yerleştirmek için bir veri yapısında tutmak da iyi bir plan veya onları bir arada tutmanın başka bir yöntemi olabilir.

Bitişiklik listesi - sadece bağlı düğümlerin bir listesi - açık farkla en verimli bellek, ancak muhtemelen en yavaş olanıdır.

Bir çizge Ters olan kolay matris sunumu ile ve kolay bitişiklik listesi ile, ancak nesne / seçilen gösterim ile çok büyük.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.