實驗項目六 圖結構基本操作的實現(截止12-20)
1.掌握圖的基本操作—遍歷。
實驗要求:
1、 分別用DFS和BFS的方法實現一個無向圖的遍歷。
實驗過程:
1、 創建一個圖(可用鄰接矩陣或鄰接表的方式進行存儲);
2、 輸入選項:0或1,0爲DFS,1爲BFS。
3、 分別輸出DFS和BFS兩種遍歷序列;
實驗報告中給出DFS和BFS兩種遍歷的算法代碼。
實驗結果:
1、輸入頂點集:1 2 3 4 5 6 7 8
2、輸入邊的集合: 1 2
1 3
2 4
2 5
4 8
5 8
3 6
3 7
6 7
輸入:0(DFS)
輸出:DFS遍歷序列爲:12485367
輸入:1(BFS)
輸出:BFS遍歷序列爲:12345678
實驗分析:
1.簡單分析DFS與BFS實現時用到的方法(DFS通過遞歸函數實現,用到棧的數據結構,BFS用到隊列的數據結構);
2.列舉調試運行過程中出現的錯誤並分析原因。
要求:
(1) 程序要添加適當的註釋,程序的書寫要採用縮進格式。
(2) 程序要具在一定的健壯性,即當輸入數據非法時,程序也能適當地做出反應。
(3) 程序要做到界面友好,在程序運行時用戶可以根據相應的提示信息進行操作。
(4) 上傳源程序到課堂派。順序表的源程序保存爲TraversalGraph.cpp。
#include <stdio.h>
#include <iostream>
#include <algorithm>
#define MaxInt 32767
#define MVNum 100
#define OK 1
#define ERROR 0
using namespace std;
typedef int Status;
//------隊列的鏈式存儲結構-------
typedef struct QNode
{
int data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct
{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
//鏈式隊列的初始化
Status InitQueue(LinkQueue &Q)
{
Q.front=Q.rear=new QNode;
Q.front->next=NULL;
return OK;
}
//鏈式隊列的入隊操作
Status EnQueue(LinkQueue &Q,int e)
{
QueuePtr p;
p=new QNode;
p->data=e;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
return OK;
}
//判斷隊列是否爲空
QueueEmpty(LinkQueue Q)
{
if(Q.front==Q.rear) return OK;
else return ERROR;
}
//鏈隊的出隊
Status DeQueue(LinkQueue &Q,int &e)
{
QueuePtr p;
if(Q.front==Q.rear) return ERROR;
p=Q.front->next;
e=p->data;
Q.front->next=p->next;
if(Q.rear==p) Q.rear=Q.front;
delete p;
return OK;
}
//圖的鄰接矩陣存儲
typedef int VerTexType;
typedef int ArcType;
typedef struct
{
VerTexType vexs[MVNum];
ArcType arcs[MVNum][MVNum];
int vexnum,arcnum;
}AMGragh;
typedef AMGragh Gragh;
bool visited[MVNum];
//確定頂點在G中的位置
int LocateVex(AMGragh G,int m)
{
return m-1;
}
//鄰接矩陣創建無向網
Status CreateUDN(AMGragh &G)
{
int i,j,k;
int v1,v2;
printf("請輸入頂點數和邊數(空格隔開):");
cin>>G.vexnum>>G.arcnum;
for(i=0;i<G.vexnum;i++)
{
printf("請輸入第%d個頂點的信息:",i+1);
cin>>G.vexs[i];
}
for(i=0;i<G.vexnum;i++)
{
for(j=0;j<G.vexnum;j++)
{
G.arcs[i][j]=0;
}
}
for(k=0;k<G.arcnum;k++)
{
printf("請輸入一條邊依附的頂點(請用空格隔開):");
cin>>v1>>v2;
i=LocateVex(G,v1);
j=LocateVex(G,v2);
G.arcs[i][j]=1;
G.arcs[j][i]=G.arcs[i][j];
}
return OK;
}
void DFS_AM(AMGragh G,int v)
{
cout<<v;
visited[v]=true;
for(int w=0;w<G.vexnum;w++)
if((G.arcs[v-1][w]!=0)&&(!visited[w+1]))
DFS_AM(G,w+1);
}
//尋找v1的第一個鄰接點
int FirstAdjVex(Gragh G,int v1)
{
for(int i=0;i<G.vexnum;i++)
{
if(G.arcs[v1-1][i]==1&&!visited[i+1])
{
return i+1;
}
}
return -1;
}
//u相對於w的下一個鄰接點
int NexAdjVex(Gragh G1,int u1,int w1)
{
for(int i=0;i<G1.vexnum;i++)
{
if(G1.arcs[u1-1][i]==1&&(i+1)!=w1&&!visited[i+1])
{
return i+1;
}
}
return -1;
}
void BFS(Gragh G,int v)
{
LinkQueue Q;
cout<<v;
visited[v]=true;
InitQueue(Q);
EnQueue(Q,v);
while(!QueueEmpty(Q))
{
int u;
DeQueue(Q,u);
for(int w=FirstAdjVex(G,u);w>=0;w=NexAdjVex(G,u,w))
{
if(!visited[w])
{
cout<<w;
visited[w]=true;
EnQueue(Q,w);
}
}
}
}
int main()
{
AMGragh G;
CreateUDN(G);
bool choose;
printf("如果你想要使用DFS遍歷請輸入0,如果你想要使用BFS遍歷輸入1:");
cin>>choose;
if(choose==0) DFS_AM(G,1);
else BFS(G,1);
return 0;
}
/*輸入緩存!當有多個輸入進入時,如果不小心多輸入,有可能會造成錯誤,使得多輸入的數據進入輸入緩存區
進而造成程序錯誤,避免這種情況的方法是:1、相同類型元素儘可能分開輸入2、相同類型數據的話,輸入一個數據後換行輸入另一個數據
一開始輸入頂點的時候8個頂點卻輸成了9個,結果第9個頂點進入緩存區,讀取成了v1,最後結果錯誤
我們輸入的字符先存放在緩衝區,等按下回車鍵換行時才進行實際讀取操作
從本質上講,我們從鍵盤輸入的數據並沒有直接交給 scanf(),而是暫時保存到緩衝區中,直到我們按下回車鍵,scanf() 才從緩衝區中讀取數據,賦值給變量。如果緩衝區中的數據符合 scanf() 的要求,那麼就讀取結束;如果不符合要求,那麼就繼續等待用戶輸入,或者乾脆讀取失敗。scanf() 匹配到想要的數據後,會將匹配到的數據從緩衝區中刪除,而沒有匹配到的數據仍然會留在緩衝區中。
正是由於緩衝區的存在,才使得我們能夠多輸入一些數據,或者一次性輸入所有數據。
2、因爲下標問題,鄰接矩陣中的邊是從下標0開始存的,而bfs輸出頂點的時候是從1開始的,因此造成了一些錯誤。
3、沒有認真思考程序的每一行代碼,原本數據類型是int類型,自己直接抄的書上的代碼結果把數據定義成了char類型
*/
這是我自己寫的!這是我自己寫的!這是我自己寫的!
開心的事要說三遍。在寫鄰接矩陣的時候,耐心的數據結構老師認真給我解答了疑惑,之後經過兩個半小時以上的努力,我終於寫完了這個程序。這對我來說是意義重大的,慾望的折磨、學習狀態的悽慘、個別人的嘲笑,讓我處於既努力自信又痛苦的尷尬境地之中,能夠獨立完成這個在周圍人看來不太簡單的任務,帶給我很大的快樂,那些不如意,此時又算得了什麼呢?
數據結構真的真的真的很簡單,不會的人可以問問自己,自己真的認真學了嗎?
我也逐漸懂得,只有學習、只有經過努力拿到不那麼容易拿到的東西,才能真正給你帶來快樂。那些容易得到的,大多隻是虛幻無意義的快感罷了。