一開始我是用c寫的,后面才發現廣搜要用到隊列,所以我就直接使用c++的STL隊列來寫,
因為不想再寫多一個隊列了。這次實驗寫了兩個多鍾,因為要邊寫邊思考,太菜了哈哈。
主要參考《大話數據結構》這本書,然后加上自己的一些東西改編,這次實驗算是完成了;
--------------------------------------------------------------------------------
首先我們來看一下鄰接表是怎么存儲圖的,比如說下面有一個無向圖
則它的鄰接表是這樣的,鄰接表有兩個部分,一個是頂點表,一個是邊表。頂點表長這樣
然后就看,它有好多個頂點,所以,我們給它開個數組,長這樣
那個下標是輸入頂點的順序,就是一個位置
上面這一坨就是鄰接表的頂點表,顧名思義,就是用來存它的頂點的一個表
然后接下來是邊表,邊表是用來干什么的呢?邊表就是用來存一個頂點的鄰接點,
如剛才的無向圖,
頂點v0的鄰接點就是v1,v3了,v2的鄰接點就是v1,v3,v4
所以由此我們可得出邊表的樣子,長這樣
邊表就是這樣子,用來存頂點的鄰接點的,每一行都是一個邊表,所以一個頂點對應一個邊表
那些箭頭就是指針,指向下一個結點,然后null表示為空
現在頂點表有了,邊表也有了,我們把它合並起來就很清楚,look;
這個就是一個圖的鄰接表了,如果是有向圖,則在adjvex和next中間加多一個weight用來存儲權值就行了,但在這我就不再講了
好了,圖出來了,看看代碼怎么實現;
首先,我們得定義好頂點表和邊表的結構,因為它們都是一種新的數據類型
代碼如下:
//邊表節點結構,一個adjvex用來存儲鄰接點的位置,一個next指針用來指向下一個節點 typedef struct EdgeNode { int adjvex; struct EdgeNode * next; } EdgeNode; //頂點表節點結構,一個data用來存儲數據,一個firstedge是用來指向邊表的第一個節點 typedef struct { string data; EdgeNode * firstedge; } AdjList;
然后我們就可以定義一個圖的鄰接表了,這樣
//里面的adjList[15]表示我給頂點表開了15的單位大小,然后numVertex,numEdge是一個圖的頂點數和邊數 typedef struct { AdjList adjList[15]; int numVertex,numEdge; } GraphAdjList;
所以后面我們想定義一個新的圖的鄰接表,可以直接 GraphAdjList G 就行了;
重點來了,定義圖的鄰接表我們搞定了,接下來就是創建了
在此之前我們得先看看一個東西
那就是寫一個函數,用來返回一個頂點所在的位置,這個會有用到
代碼如下:
//這個函數是這樣的,它會遍歷圖的頂點,然后返回一個位置(其實也就是它所在的下標) int local(GraphAdjList G,string val) { for(int i=0; i<G.numVertex; i++) { if(G.adjList[i].data==val) return i; } return -1; } //比如v2的位置是在2 這個可以看上面的頂點表圖
接下來就是創建一個圖的鄰接表,我會邊用代碼邊用圖來表示,這樣就可以更好的理解代碼了
創建圖的代碼如下
void CreateGraph(GraphAdjList & G) { int i,j,k; string v1,v2; EdgeNode * e,* p,*q; cout<<"請輸入頂點數和邊數,並以空格隔開:"<<endl; cin>>G.numVertex>>G.numEdge; cout<<"請輸入頂點的信息:"<<endl; for(i=0; i<(G.numVertex); i++) { cout<<"第"<<i+1<<"個頂點:"<<endl; cin>>G.adjList[i].data; G.adjList[i].firstedge=NULL; } for(k=0; k<(G.numEdge); k++) { cout<<"請輸入邊(Vi,Vj)上的頂點信息:"<<endl; cin>>v1>>v2; i=local(G,v1); j=local(G,v2); if(G.adjList[i].firstedge==NULL) { e= new EdgeNode; e->adjvex=j; e->next=NULL; G.adjList[i].firstedge=e; } else { p=G.adjList[i].firstedge; while(p->next!=NULL) { p=p->next; } e = new EdgeNode; e->adjvex=j; e->next=NULL; p->next=e; } if(G.adjList[j].firstedge==NULL) { e= new EdgeNode; e->adjvex=i; e->next=NULL; G.adjList[j].firstedge=e; } else { p=G.adjList[j].firstedge; while(p->next!=NULL) { p=p->next; } e = new EdgeNode; e->adjvex=i; e->next=NULL; p->next=e; } } }
接下來我們拆分一些主要代碼,看看是怎么實現的;
①
cin>>G.adjList[i].data;
G.adjList[i].firstedge=NULL;
這里是往頂點表插入頂點,並把firstedge域置空,如圖:
②
cin>>v1>>v2;
i=local(G,v1);
j=local(G,v2);
輸入每條邊的兩端的頂點,一般我們是從小到大輸入的,例如這里我們
先是輸入v0 v1 , v0 v3這樣的,然后它會返回位置分別是0 1,0 3
然后后面邊表的插入方法時,我用的是尾插法,因為這樣才能達到跟圖片一樣的效果
不然用頭插法的話,那個邊表的順序是反的;
當我們執行完這個CreateGraph函數的時候,就已經創建了如下圖所示的鄰接表了;
創建完成后,我們就可以把鄰接表輸出了,而且是按照圖中所示一樣的輸出
代碼如下:
void Prin(GraphAdjList G) { cout<<"所建立的鄰接表如以下所示:"<<endl; for(int i=0; i<G.numVertex; i++) { cout<<G.adjList[i].data; //先輸出頂點信息 EdgeNode * e = G.adjList[i].firstedge; while(e) //然后就開始遍歷輸出每個邊表所存儲的鄰接點的下標 { cout<<"-->"<<e->adjvex; e=e->next; } cout<<endl; } }
至此,我們創建一個圖的鄰接表並可以輸出該鄰接表了,接下來我們
就是廣度優先遍歷和深度優先遍歷了
關於這兩個遍歷的定義我也不多講了,你們可以查資料,我直接就貼
上代碼就好了
①深度優先遍歷
這個遍歷的重點就是它的算法
void DFS(GraphAdjList G,int i) { EdgeNode * p; DFSvisited[i]=true; cout<<G.adjList[i].data<<" "; p=G.adjList[i].firstedge; while(p) { if(!DFSvisited[p->adjvex]) DFS(G,p->adjvex); p=p->next; } }
然后就是使用這個算法了
void DFSTraverse(GraphAdjList G) { for(int i=0; i<G.numVertex; i++) DFSvisited[i]=false; for(int i=0; i<G.numVertex; i++) { if(!DFSvisited[i]) DFS(G,i); } }
所以這兩部分代碼結合起來就是深度優先遍歷了
對了,那個DFSvisited[i]是一個bool型的數組,
用來標記是否遍歷過的,可以在前面加上
bool DFSvisited[50]; //用於深搜的標記數組 bool BFSvisited[50]; //用於廣搜的標記數組
②廣度優先遍歷
這個遍歷直接一個函數就能搞定,需要用到隊列,
相信你們既然在看圖的部分的話,那么隊列肯定
也早已學會了,嘿嘿。
void BFSTraverse(GraphAdjList G) { EdgeNode * p; queue<int>q; for(int i=0; i<G.numVertex; i++) BFSvisited[i]=false; for(int i=0; i<G.numVertex; i++) { if(!BFSvisited[i]) { BFSvisited[i]=true; cout<<G.adjList[i].data<<" "; q.push(i); while(!q.empty()) { int count =q.front(); q.pop(); p=G.adjList[count].firstedge; while(p) { if(!BFSvisited[p->adjvex]) { BFSvisited[p->adjvex]=true; cout<<G.adjList[p->adjvex].data<<" "; q.push(p->adjvex); } p=p->next; } } } } }
啊咧咧,還有一個銷毀圖的操作
void DestoryGraph(GraphAdjList & G) { EdgeNode * p = NULL; for(int i=0; i<G.numVertex; i++) { p=G.adjList[i].firstedge; while(p) { EdgeNode * temp = p; p=p->next; delete temp; } G.adjList[i].firstedge=NULL; } }
至此,整篇文章可以結束了,有什么錯誤或者問題可在下方留言,
一起探討
貼上測試效果圖:
你們可以注意一下鄰接表的輸出跟我上面給的圖是一樣的
拜拜,我去做大保健了。