簡體   English   中英

圖形實現和鄰接矩陣的初始化

[英]Graph implementations and initialization of adjacency matrices

圖通常使用鄰接矩陣表示。 各種來源表明可以避免初始化成本為| V ^ 2 | V是頂點的數量),但我可能還沒弄清楚如何。

在Java中,只需通過聲明矩陣,例如boolean adj [][] ,運行時將自動使用false初始化數組,這將是O(V ^ 2)成本(數組的維度)。
我誤解了嗎? 是否有可能避免鄰接矩陣初始化的二次成本,或者這只是理論依賴於實現語言的東西?

這可以通過使用鄰接矩陣的稀疏矩陣表示來實現,其中僅分配“1”的位置而不是矩陣的每個元素(可能包括大量的零)。 您可能會發現此線程也很有用

矩陣值的默認初始化實際上是一個特征。 如果不是默認初始化,您是否仍然需要自己初始化每個字段,以便您知道它的價值是什么?

鄰接矩陣有這樣的缺點:它們在內存效率方面很差(它們需要O(n 2 )個存儲單元)並且如你所說它們的初始化速度較慢。 然而,初始化從未被認為是最大的問題。 相信我,內存分配要慢得多,並且所需的內存比初始化時間更具限制性。


在許多情況下,人們更喜歡使用鄰接列表而不是矩陣。 這樣的列表需要O(m)存儲器,其中m是圖中邊的數量。 這樣效率更高,特別是對於稀疏圖。 該圖表示僅比鄰接矩陣更差的唯一操作是查詢is there edge between vertices i and j 矩陣在O(1)時間內回答,列表肯定會慢得多。

然而,許多典型的圖算法(如DijkstraBellman-FordPrimTarjanBFSDFS )只需要迭代給定頂點的所有鄰居。 如果使用鄰接列表而不是矩陣,所有這些算法都會受益匪淺。

這個帖子中存在很多混亂和錯誤的信息。 實際上,存在一種避免鄰接矩陣(以及通常的任何陣列)的初始化成本的方法。 但是,不可能將該方法與Java原語一起使用,因為它們是使用默認值初始化的。

假設您可以創建一個未自動初始化的數組data[0..n] 首先,它充滿了以前在內存中的垃圾。 如果我們不想花費O(n)時間來覆蓋它,我們需要一些方法來區分我們從垃圾數據中添加的好數據。

“技巧”是使用輔助堆棧來跟蹤包含良好數據的單元格。 我們第一次寫入data[i] ,我們將索引i添加到堆棧中。 由於堆棧只在我們添加它時才會增長,因此它永遠不會包含我們需要擔心的任何垃圾。

現在,每當我們訪問data[k]我們可以檢查其垃圾或不通過掃描堆棧k 但是每次讀取都需要O(n)時間,首先要擊敗陣列!

為了解決這個問題,我們制作另一個輔助數組stack_check[0..n] ,它也開始充滿垃圾。 此數組包含指向堆棧中元素的指針。 現在,當我們第一次寫入data[i] ,我們將i推入堆棧並將stack_check[i]設置為指向新的堆棧元素。

如果data[k]是好數據,則stack_check[k]指向保持k的堆棧元素。 如果data[k]是垃圾,那么stack_check[k]的垃圾值要么指向​​堆棧外部,要么指向除k之外的某個堆棧元素(因為k從未放在堆棧上)。 檢查此屬性只需要O(1)時間,因此我們的數組訪問速度仍然很快。

將它們整合在一起,我們可以在O(1)時間內創建我們的數組和輔助結構,讓它們充​​滿垃圾。 在每次讀寫時,我們使用我們的幫助器檢查給定單元格是否在O(1)時間內包含垃圾。 如果我們正在編寫垃圾,我們更新我們的幫助器結構以將單元格標記為有效數據。 如果我們閱讀垃圾,我們可以以適合給定問題的任何方式處理垃圾。 例如,我們可以返回一個默認值,如0(現在你甚至不能告訴我們沒有初始化它!)或者可能拋出異常。

我將詳細說明A_A的答案。 他建議使用稀疏矩陣,這基本上意味着你要回到維護鄰接列表。

使用矩陣有兩個原因 - 如果你根本不關心性能,就像它提供的簡單代碼,或者如果你關心性能,但你的矩陣將相對充滿(假設至少20%)滿滿的,為了這個帖子)。

你顯然關心性能。 如果你的矩陣相對空,它的初始化開銷可能是有意義的,你最好使用鄰接列表。 如果它會非常滿,初始化變得可以忽略不計 - 你需要在矩陣中填充正確的單元格(這不僅需要初始化它),而且你需要處理它們(這需要花費更多的時間)初始化它)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM