1. 圖的鄰接表存儲結構 表頭結點後面跟的鄰接結點的排列先後順序有要求嗎
可以,這個沒有什麼區別。
四種表示圖的方法:
1.鄰接矩陣
2.鄰接表
3.鄰接多重表
4.十字鏈表
2. 求大蝦解答【數據結構】判斷題
1對。
2對
3錯;數組中盡量避免插入和刪除操作
4對
5錯
6對;LBR和LRB沒B時遍歷時一樣。L左R右B根
7.錯
8對。
9錯
10錯。
3. 鄰接表的表示法
注意:
n個頂點e條邊的無向圖的鄰接表表示中有n個頂點表結點和2e個邊表結點。(換句話說,每條邊(i,j)在鄰接表 中出現兩次:一次在關於i的鄰接表中,另一次在關於j的鄰接表中) 對於有向圖,vi的鄰接表中每個表結點都對應於以vi為始點射出的一條邊。因此,將有向圖的鄰接表稱為出邊表。
【例】有向圖G6如下圖所示,其中頂點v1的鄰接表上兩個表結點中的頂點序號分別為0和4,它們分別表示從v1射出的兩條邊(簡稱為v1的出邊):<v1,v0>和<v1,v4>。
注意:
n個頂點e條邊的有向圖,它的鄰接表表示中有n個頂點表結點和e個邊表結點。(因為有向圖是單向的) 在有向圖中,為圖中每個頂點vi建立一個入邊表的方法稱逆鄰接表表示法。
入邊表中的每個表結點均對應一條以vi為終點(即射入vi)的邊。
【例】G6的逆鄰表如上面(b)圖所示,其中v0的入邊表上兩個表結點1和3分別表示射人v0的兩條邊(簡稱為v0的入邊):<v1,v0>和<v3,v0>。
注意:
n個頂點e條邊的有向圖,它的逆鄰接表表示中有n個頂點表結點和e個邊表結點。
3.鄰接表的形式說明。
鄰接表是一個二維容器,第一維描述某個點,第二維描述這個點所對應的邊集們。
實現鄰接表的方法絕對有100種以上。即使是前向星這種東西也是鄰接表,因為它還是描述某個點和這個點所對應的邊集們.
我們說說常用的鄰接表存圖法(靜態的array就不說了.)必須有開O1以及以上編譯的條件,不然沒有測試的效率無任何意義。
第一維是描述點的。可以用vector,list,forward_list,deque,map,multimap,unordered_map,unordered_multimap等(一般不能用set,mutiset,unordered_set,unordered_multiset).
按照你的要求去選擇。一般來講存完圖以後不涉及點的加入與刪除優先使用vector.map,multimap,unordered_map,unordered_multimap.
第二維是描述這個點的邊集,可以用全部的容器。也是的一般來講存完圖以後,不涉及點的加入與刪除優先使用vector,空間充足可以考慮deque.涉及點的刪除用forward_list或者是list,map,multimap,unordered_map,unordered_multimap.
對於這個圖存儲的方法舉例(對於上面的圖):
第一種: #include<vector>#include<iostream>usingnamespacestd;intmain(){vector<vector<size_t>>graph(5);graph[0].push_back(1);//V0->V1.graph[1].push_back(4);//V1->V4.graph[1].push_back(0);//V1->V0.graph[2].push_back(1);//V2->V1.graph[2].push_back(3);//V2->V3.graph[3].push_back(0);//V3->V0.graph[4].push_back(3);//V4->V3.//假定要訪問點1.for(constauto&ele:graph[1])//對於全部屬於graph[1]這個容器的元素std::cout<<ele<<'';std::cout<<std::endl;//程序運行後輸出40,表示點1連接了4點和0點。return0;}對第一種方法有種優化就是對每個點所對應的邊的向量進行預估。例如有m條有向邊,n個點,那麼每個向量需要reserve(6*(m/n)/5);一般這樣存儲效率會有顯著提高。
第二種(使用map,set): #include<map>#include<set>#include<iostream>#include<cstddef>#include<map>#include<set>intmain(){std::map<std::size_t,std::set<std::size_t>>graph;graph[0].insert(1);//V0->V1.graph[1].insert(4);//V1->V4.graph[1].insert(0);//V1->V0.graph[2].insert(1);//V2->V1.graph[2].insert(3);//V2->V3.graph[3].insert(0);//V3->V0.graph[4].insert(3);//V4->V3.//假定要訪問點1.for(constauto&ele:graph[1])//對於全部屬於graph[1]這個容器的元素std::cout<<ele<<'';std::cout<<std::endl;//程序運行後輸出04,表示點1連接了0點和4點。對map,set里的元素進行遍歷是有序的return0;}方法太多,不再舉例了。
然而我們這樣存圖是不夠的,對於無向圖而言,可能存在一條邊需要知道自己的反向邊的位置。例如歐拉迴路,例如網路流都是需要的。
第二種方法由於std::map<std::size_t,std::set<std::size_t>> graph;只是離散化後的鄰接矩陣。對於這種存圖方法,訪問反向邊則非常簡單,例如我訪問的是a->b這樣一條邊。那麼只需要graph[b].count(a);就可以訪問其反向邊b->a。
然而對於第一種方法,我們沒有辦法解決反向邊的互相訪問題。
所以我們對於這種圖需要存圖修正。代碼如下: #include<vector>#include<iostream>#include<utility>#include<cstddef>intmain(){std::vector<std::vector<std::pair<std::size_t,std::size_t>>>graph(5);//每條邊的第二個元素不能是迭代器!!會失效!!!!必須是下標!!!//比如有一條a-b的邊,我們得讓它實現互訪。我們這里假定a=2,b=4;std::size_ta(2),b(4);graph[a].push_back(std::make_pair(b,graph[b].size()+(a==b)));//Va->Vb.需要判定a是否等於b.graph[b].push_back(std::make_pair(a,graph[a].size()-1));//Vb->Va,這個不需要判定a是否等於b.//訪問反向邊的方法.for(constauto&ele:graph[a])//訪問點agraph[ele.first][ele.second];//這就是邊ele的反向邊了return0;}對於list容器可以直接存迭代器的,但是存圖時也得考慮a是否等於b.forward_list存反向邊的圖就不好,因為用鏈表存圖就是需要存完圖後插入刪除,假定一個元素前面的元素被刪除了,那麼根本無法訪問反向邊!!!!
感覺存圖沒問題了?NO!!!!還有一種圖更奇葩,那就是對於每個點中的邊得排序又得知道反向邊的那種圖。USACO上有個題目叫做騎馬修柵欄,那個題要求字典序輸出。數據量很小,以至於可以直接矩陣存圖,但是我們考慮如何數據量大,這種方法就不行了。如果用第二種方法(std::map<std::size_t,std::set<std::size_t>>)存圖,絕對可可以,但是相對常數較大。如果我們考慮順序容器去存儲則比較快一些。
方法就是先用邊集表存圖,然後每條邊a,b得優先以std::min(a,b)為第一關鍵字再按std::max(a,b)為第二關鍵字排序,再按照修正後的存圖方法存圖即可。具體代碼見nocow上騎馬修柵欄那題lgeecn發的題解和代碼。
如果使用list存圖可以先存圖再用list.sort()函數進行排序,不過似乎效率會差一些,畢竟相對於vector,list常數太大了,達到6倍以上。
存圖真心不簡單,因為真正用的時候你可能會遇到各種問題,但是你可以加以思考,進行容器搭配使用,即可解決。
4. 關於數據結構中鄰接表的問題
鄰接表是圖的一種鏈接存儲結構。在鄰接表中,對圖中每個頂點建立一個帶頭結點的單鏈表,所有的頭結點構成一個數組,第i個單鏈表中的結點表示依附於頂點vi的邊。也就是說指的是點,表示的是邊,因為兩點決定了一條邊。以下圖為例:
與0號點相連的有2條邊,一條與1號點相連,一條與3號點相連。所以v0後面有兩個結點,前面的序號分別為1、3,3後沒有了,為空;
與1號點相連的有3條邊,分別與0、2、3號點相連。所以v0後面有3個結點,前面的序號分別為0、2、3,3後沒有了,為空;
與2號點相連的有1條邊,與1號點相連。所以v0後面有1個結點,前面的序號為1,1後沒有了,為空。
對照圖好好理解,應該能明白了。
5. 圖的鄰接表是否沒有先後順序
通常的建表方式無論有向還是無向是不考慮順序問題的,因為鄰接表表示的僅僅是兩個節點是否相連以及它們的權值,即使加上順序也沒有什麼用處.不過要是為了輸出看著好看的話,可以在建表的時候加上類似插入排序的演算法結構,讓每個節點對應的一串是有序的.
6. c語言,關於鄰接表的建立
AdjList 是自定義類型,是char類型,
第二行 typedef將char類型自定義為 VertexType,即VertexType代表了char型,
VertexType a 就相當於 char a;
同理
倒數第五行 typedef將VertexNode自定義為 AdjList[MaxVertexNum]類型,也就是說現在AdjList就代表了一個 「結構體數組」 類型
AdjList adjlist 相當於 VertexNode adjlist[MaxVertexNum]
這里主要是typedef關鍵字的使用 希望能幫到你
7. 採用鄰接表存儲的圖的深度優先遍歷演算法類似於二叉樹的先序遍歷,為什麼是先序呢
這是因為圖的深度優先遍歷演算法先訪問所在結點,再訪問它的鄰接點。與二叉樹的先序遍歷先訪問子樹的根結點,再訪問它的孩子結點(鄰接點)類似。圖的廣度優先遍歷演算法類似於二叉樹的按層次遍歷。
先序遍歷也叫做先根遍歷、前序遍歷,可記做根左右(二叉樹父結點向下先左後右)。
首先訪問根結點然後遍歷左子樹,最後遍歷右子樹。在遍歷左、右子樹時,仍然先訪問根結點,然後遍歷左子樹,最後遍歷右子樹,如果二叉樹為空則返回。
例如,下圖所示二叉樹的遍歷結果是:ABDECF。
(7)領接表存儲必須有序嘛擴展閱讀:
遍歷種類:
一、NLR:前序遍歷(Preorder
Traversal
亦稱(先序遍歷)),訪問根結點的操作發生在遍歷其左右子樹之前。
二、LNR:中序遍歷(Inorder
Traversal),訪問根結點的操作發生在遍歷其左右子樹之中(間)。
三、LRN:後序遍歷(Postorder
Traversal),訪問根結點的操作發生在遍歷其左右子樹之後。
注意:
由於被訪問的結點必是某子樹的根,所以N(Node)、L(Left
subtree)和R(Right
subtree)又可解釋為根、根的左子樹和根的右子樹。NLR、LNR和LRN分別又稱為
先根遍歷、中根遍歷和後根遍歷。
參考資料來源:網路-先序遍歷