用鄰接連結串列儲存無向圖和有向圖
阿新 • • 發佈:2019-01-22
上一篇文章我們說到用鄰接矩陣儲存有向圖和無向圖,這種方法的有點是很直觀的知道點與點之間的關係,查詢的複雜度是為O(1)。但是這種儲存方式同時存在著佔用較多儲存空間的問題,
鄰接矩陣儲存很好理解,但是,有時候太浪費空間了,特別是對於頂點數多,但是關聯關係少的圖。
舉個極端的例子。
下圖中,5個頂點都是孤立的,然而為了儲存邊的資訊,要分配一個5X5的矩陣。本來一條邊都沒有,沒必要用儲存空間來儲存邊,但還要分配額外的空間。
用鄰接表則可以避免這種浪費。鄰接表用動態表結構儲存邊,更加靈活:有一個邊就分配一個空間去儲存,沒有就不分配。
我們仍然用鄰接矩陣中那個圖的例子來說明鄰接表的構建思想。
鄰接表只需要一個數組,但是這個陣列有點複雜,需要解剖一下。如下圖。
可以發現,用鄰接表儲存時,圖中的每一個節點 不僅要儲存本頂點的資料data,還要儲存一個表,這個表儲存了此頂點的所有臨接點的索引。2點確定一線,那麼就能儲存此節點相關的所有的邊了。
下面我們用到C++庫中的vector來實現indexlist。程式碼如下:
2、有向網用鄰接連結串列儲存#include<stdio.h> #include<vector> #define MAX_VERTEX 4 using std::vector; //使用C++標準庫中的vector來作為list,它實質就是一個順序表 typedef char DataType; typedef struct node { DataType data; //頂點資料 vector<int> indexList; //儲存頂點鄰接點索引的 表 }Node; typedef Node* UListGraph; /********************************/ void UListGraph_init(UListGraph*pGraph); void UListGraph_create(UListGraph graph); void UListGraph_show(UListGraph graph); void UListGraph_destroy(UListGraph*pGraph); int main(void) { UListGraph g; UListGraph_init(&g); UListGraph_create(g); UListGraph_show(g); UListGraph_destroy(&g); return 0; } void UListGraph_init(UListGraph*pGraph) { (*pGraph) = new Node[MAX_VERTEX]; } void UListGraph_create(UListGraph graph) { for (int i = 0; i < MAX_VERTEX; ++i) //填充頂點陣列 { printf("輸入第%d個頂點值\n",i+1); scanf(" %c", &(graph[i].data)); } for(int i=0;i<MAX_VERTEX;++i) for(int j=i+1;j<MAX_VERTEX;++j) { printf("如果元素%c和%c之間有邊,請輸入1,否則輸入0",graph[i].data,graph[j].data); int opt; scanf("%d",&opt); if(opt==1) { graph[i].indexList.push_back(j); graph[j].indexList.push_back(i); } } } void UListGraph_show(UListGraph graph) { printf("頂點元素如下:\n\n"); for(int i=0;i<MAX_VERTEX;i++) { printf("%c\t",graph[i].data); } printf("\n\n"); for(int i=0;i<MAX_VERTEX;i++) { printf("元素%c的鄰接點 :",graph[i].data); Node tnode = graph[i]; for(int k=0;k<tnode.indexList.size();++k) { printf("%c\t",graph[tnode.indexList[k]].data); } putchar('\n'); } } void UListGraph_destroy(UListGraph*pGraph) { delete (*pGraph); *pGraph = NULL; }
鄰接表對於無向圖來說很適用,但是對於有向圖來說就不適用了,因為鄰接表中,每一個節點的IndexList只能儲存一種狀態的弧,出弧或者入弧(逆鄰接表),那怎麼將一個頂點的出入弧都儲存起來呢?
那就是將鄰接表和逆鄰接表都用起來,也就是一個節點需要儲存:①data,②inArcList,入弧記錄表,③outArcList,出弧記錄表
實現程式碼如下:
#include<stdio.h> #include<vector> using std::vector; #define MAX_VERTEX 4 typedef char DataType; typedef struct arc{ int headVertex; //此條弧的頭尾結點的index int tailVertex; //此條弧的頭尾結點的index //WeightType weight; //此弧的附加資訊,比如權重,這裡是有向圖,不是網,所以不需要weight }Arc; typedef struct node{ DataType data; //頂點資料域 vector<Arc> outArc; //此頂點的所有 出度 弧 表 vector<Arc> inArc; //此頂點的所有 入度 弧 表 }Node; typedef Node* Graph; void Graph_init(Graph* pG); void Graph_create(Graph g); void Graph_show(Graph g); void Graph_destroy(Graph*pg); int main(void) { Graph g; Graph_init(&g); Graph_create(g); Graph_show(g); return 0; } void Graph_init(Graph* pG) { (*pG) = new Node[MAX_VERTEX]; if(*pG == NULL) { //exit(-1); } } void Graph_create(Graph g) { int opt; for (int i = 0; i < MAX_VERTEX; ++i) //填充頂點陣列 { printf("輸入第%d個頂點值\n",i+1); scanf(" %c", &(g[i].data)); } for(int j=0;j<MAX_VERTEX;++j) for(int i=j+1;i<MAX_VERTEX;++i) { printf("若元素%c有指向%c的弧,則輸入1,否則輸入0\t",g[j].data,g[i].data); scanf("%d",&opt); if(opt==1) { Arc* parc = new Arc; parc->headVertex = i; parc->tailVertex = j; g[j].outArc.push_back(*parc); g[i].inArc.push_back(*parc); } printf("若元素%c有指向%c的弧,則輸入1,否則輸入0\t",g[i].data,g[j].data); scanf("%d",&opt); if(opt==1) { Arc* parc = new Arc; parc->headVertex = j; parc->tailVertex = i; g[i].outArc.push_back(*parc); g[j].inArc.push_back(*parc); } } } void Graph_show(Graph g) { for (int j = 0; j <MAX_VERTEX; ++j) { printf("頂點資料%c\n",g[j].data); printf("發射:"); for(int i=0;i<g[j].outArc.size();++i) { printf("%c\t",g[g[j].outArc[i].headVertex].data); } putchar('\n'); printf("接收:"); for(int i=0;i<g[j].inArc.size();++i) { printf("%c\t",g[g[j].inArc[i].tailVertex].data); } printf("\n\n"); } } void Graph_destroy(Graph*pg) { delete (*pg); *pg = NULL; }