圖——數據結構 逆鄰接表與十字鏈表


前言:如果你已經學習了鄰接表的存儲思想,那么逆鄰接表也非常好理解,我們的重點是十字鏈表

  首先我們來繼續介紹逆鄰接表,逆鄰接表和鄰接表是一樣的,只不過在鄰接表上,一個頂點后面連接的一串節點都是以頂點為弧尾的弧頭節點,我們建立鄰接表的時候就先查找一條邊的起點,然后往這個起點上連接新的頂點,那么逆鄰接表就是反過來,逆鄰接表中一個頂點后面的一串節點都是以頂點為弧頭的弧尾節點,我們建立逆鄰接表的時候,先查找一條邊的終點,然后往這個重點上連接包含起點信息的節點;

  逆鄰接表就介紹到這里,下面我們介紹十字鏈表,首先我們拋出這樣一句話,十字鏈表是正鄰接表和逆鄰接表的合體,然后我們再介紹這樣一個觀念,我們在逆鄰接表或者鄰接表中所提到的節點,其實可以理解為弧節點,弧節點可以包含弧頭頂點在圖中的位置(我們在正鄰接表中遇到的),也可以包含弧尾頂點在圖中的位置(逆鄰接表);下面我們給出具體的例子幫助大家理解

  現在思考下當我們要建立十字鏈表時會發生些什么:首先我們建立好頂點數組,和在鄰接表中的一樣,然后呢,我們開始添加 e01 這個邊,或者說弧現在我們要用弧節點的思路去理解他,如何添加這個弧,這個弧包含了哪些信息?弧包含了tail,代表弧尾表示的頂點在圖中的位置,也就是這個弧是由誰發射出來的,head呢,表示這個弧指向了誰,同樣儲存的是頂點在頂點數組中的索引;后面這兩項是地址,是什么類型的地址呢,這個地址指向的空間要放什么東西呢,放的仍然是弧節點,但是這兩個地址所放的不是同一個弧節點,這個tlink,指向的是下一個弧節點,tlink指向的這個弧節點的弧尾和tlink所在的弧的弧尾是相同的,按照這樣的想法,我們很容易想到沿着tlink一路走下去,我們就可以遍歷由同一個頂點發射的所有弧,相當於是一個正鄰接表,同理沿着hlink我們會遍歷指向同一個頂點的所有弧,而一個弧節點既有hlink,又有tlink,所以正鄰接表和逆鄰接表是交叉的,這就叫做十字鏈表,

  那么如何來具體的建立一個十字鏈表呢,回顧上一節我們提到的,先建立頂點數組,然后按照用戶輸入的邊去建立弧節點,然后給弧節點填充相應的信息,再把弧節點鏈接到已有的十字鏈表上,鏈接的時候又需要分情況,我這個弧節點是不是處於鄰接表的第一個或者是逆鄰接表的第一個,如果是的話,需要操作頂點的firstin或者firstout,如果不是的話需要操作”最后一個“弧節點,那么這里和上一節的操作一樣,我們仍然需要去標記一下”最后一個弧節點“,只不過這里不僅要標記正鄰接表的最后一個弧節點,還需要標記逆鄰接表中處於最后一個的弧節點,

  如此如此這番這番,思路就躍然紙上了(具體化了),下面我們給出具體代碼,並針對上一節中,判定是否一個頂點沒有弧節點相連(十字鏈表中其實是沒有正鄰接表‘也就是弧尾’或逆鄰接表‘也就是弧頭’相連,或者兩個都沒有)的判定進行了更正;

  1 //驗證圖的十字鏈表存儲
  2 #include <stdio.h>
  3 #include <windows.h>
  4 #define vexNum 10
  5 
  6 typedef struct ArcNode
  7 {
  8     //索引
  9     int tailVex;
 10     int headVex;
 11 
 12     //地址,按照tlink,可以一路遍歷完正鄰接表
 13     struct ArcNode *tlink;
 14     struct ArcNode *hlink;
 15 
 16     //info代表弧的權重
 17     int info;
 18 
 19 } ArcNode;
 20 
 21 typedef struct VexNode
 22 {
 23     char data;
 24     ArcNode *last1End; //正鄰接表的最后一個弧節點
 25     ArcNode *last2End; //逆鄰接表的最后一個弧節點
 26     ArcNode *second1;  //正、第一個弧節點
 27     ArcNode *second2;  //逆、第一個弧節點
 28 } VexNode;
 29 
 30 typedef struct graph
 31 {
 32     int vexN;
 33     int edgeN;
 34     VexNode adjList[vexNum];
 35 } graph;
 36 
 37 //尋找頂點在圖中的位置
 38 int locatVex(char vex, graph g)
 39 {
 40     int i = 0;
 41     for (i; i < g.vexN; i++)
 42     {
 43         if (vex == g.adjList[i].data)
 44         {
 45             return i;
 46         }
 47     }
 48     return -1;
 49 }
 50 void creatOLGraph(graph *g)
 51 {
 52     printf("請輸入節點數和邊數:\n");
 53     scanf("%d %d", &g->vexN, &g->edgeN);
 54     getchar();
 55     printf("請輸入節點數組,不帶空格\n");
 56     int i = 0;
 57     for (i; i < g->vexN; i++)
 58     {
 59         scanf("%c", &g->adjList[i].data);
 60         g->adjList[i].second1 = NULL;
 61         g->adjList[i].second2 = NULL;
 62         g->adjList[i].last1End = NULL;
 63         g->adjList[i].last2End = NULL;
 64     }
 65     getchar();
 66     printf("請輸入邊:\n");
 67 
 68     int v1, v2;
 69     char vv1, vv2;
 70     for (i = 0; i < g->edgeN; i++)
 71     {
 72         scanf("%c %c", &vv1, &vv2);
 73         getchar();
 74         v1 = locatVex(vv1, *g);
 75         v2 = locatVex(vv2, *g);
 76         ArcNode *p;
 77         p = (ArcNode *)malloc(sizeof(ArcNode));
 78 
 79         //同一個弧節點,先對正鄰接表操作,再對逆鄰接表操作
 80 
 81         //弧節點的弧尾是v1
 82         p->tailVex = v1;
 83         if (g->adjList[v1].last1End == NULL)
 84         {
 85             //頂點的第一個弧節點是p,該頂點正鄰接表的最后一個弧節點也是p
 86             g->adjList[v1].second1 = p;
 87             g->adjList[v1].last1End = p;
 88         }
 89         else
 90         {
 91             //向正鄰接表中添加一個元素,
 92             g->adjList[v1].last1End->tlink = p;
 93             g->adjList[v1].last1End = p;
 94         }
 95 
 96         p->headVex = v2;
 97         if (g->adjList[v2].last2End == NULL)
 98         {
 99             g->adjList[v2].second2 = p;
100             g->adjList[v2].last2End = p;
101         }
102         else
103         {
104             g->adjList[v2].last1End->hlink = p;
105             g->adjList[v2].last1End = p;
106         }
107     }
108 }
109 
110 int main()
111 {
112     graph g;
113     creatOLGraph(&g);
114     //驗證十字鏈表的存儲
115     int i = 0;
116     for (i; i < g.vexN; i++)
117     {
118         ArcNode *show;
119         //之前我們做判斷,是判斷“最后一個節點”是否為頂點,現在我們更改了初始化條件,把之前的初始化為頂點地址改成了初始化為NULL這就避免了類型不兼容的警告
120         if (g.adjList[i].last2End != NULL)
121         {
122             printf("發往%c的弧,弧尾是 ", g.adjList[i].data);
123             show = g.adjList[i].second2;
124             while (show != g.adjList[i].last2End)
125             {
126                 printf("%c、", g.adjList[show->tailVex].data);
127                 show = show->hlink;
128             }
129             printf("%c\n", g.adjList[show->tailVex].data);
130         }
131 
132         if (g.adjList[i].last1End != NULL)
133         {
134             printf("由%c發出的弧的弧頭是 ", g.adjList[i].data);
135             show = g.adjList[i].second1;
136             while (show != g.adjList[i].last1End)
137             {
138                 printf("%c、", g.adjList[show->headVex].data);
139                 show = show->tlink;
140             }
141             printf("%c\n", g.adjList[show->headVex].data);
142         }
143     }
144     system("pause");
145     return 0;
146 }

參考:《新編數據結構案例教程(C/C++)版》薛曉亞主編

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM