資料結構實驗三目錄
- 0.實驗要求
- 1.實驗思路
- 1.1實作圖的存盤
- 1.1.1 圖的鄰接矩陣存盤
- 1.1.2 使用圖的鄰接矩陣建立一個圖
- 1.1.3 圖的鄰接表存盤
- 1.1.4 使用圖的鄰接表建立一個圖
- 1.2. 圖的鄰接矩陣和圖的鄰接表兩種存盤結構的轉換
- 1.2.1 圖的鄰接矩陣轉向圖的鄰接表
- 1.2.2 圖的鄰接表轉向圖的鄰接矩陣
- 1.3 圖的深度優先遍歷
- 1.3.1 鄰接矩陣表示的圖的深度優先遍歷
- 1.3.1.1 遞回
- 1.3.1.2 非遞回
- 1.3.2 鄰接表表示的圖的深度優先遍歷
- 1.3.2.1 遞回
- 1.3.2.2 非遞回
- 1.4圖的廣度優先遍歷
- 1.4.1 鄰接矩陣表示的圖的廣度優先遍歷
- 1.4.2鄰接表表示的圖的廣度優先遍歷
- 1.5主程式
- 1.6結果分析
0.實驗要求

1.實驗思路
1.1實作圖的存盤
1.1.1 圖的鄰接矩陣存盤
圖的鄰接矩陣存盤就是,把圖看成是一個二維矩陣,矩陣A[i][j]的值就表示節點i和節點j之間的邊的權重,如果頂點i和頂點j之間沒有邊,則設定A[i][j] = 正無窮大,
因此,用鄰接矩陣的形式存盤圖的結構,我們需要以下三個資訊
- 鄰接矩陣
- 所有的頂點的資訊
- 圖的頂點個數和邊的個數
#include<iostream>
#include<fstream>
#include<queue>
#include<stack>
#define max 100
typedef int data;//權重的資料型別
using namespace std;
bool visited[max];
int dfn[max];//頂點的先深編號
int count=1;
//鄰接矩陣的存盤結構
typedef struct{
int vertex[max];//鄰接矩陣的頂點陣列
data edge[max][max];//鄰接矩陣的邊值
int n,e;//鄰接矩陣的頂點和邊的個數
}mygraph;
看一個實體,假設邊的權重為1表示兩個頂點之間有邊,邊的權重為0表示兩個頂點之間沒有邊,

1.1.2 使用圖的鄰接矩陣建立一個圖
- 指定圖的頂點的個數和邊的個數
- 初始化鄰接表和鄰接矩陣
- 根據給定的頂點和頂點的權重改變鄰接矩陣的值, 注意:對于無向圖來說,鄰接矩陣是對稱的,所以如果頂點i , j之間權重為k,則A[i][j] = A[j][i] = k
void CreateGraph(mygraph *G){// 創建鄰接矩陣
int i,j,k,w;
cout<<"輸入頂點個數和邊的個數"<<endl;
cin>>G->n>>G->e;//輸入頂點個數和邊的個數;
for(i=0;i<G->n;i++){
G->vertex[i]=i;
}
for(i=0;i<G->n ;i++)
for(j=0;j<G->n ;j++)
G->edge[i][j]=0;//初始化鄰接矩陣
for(k=0;k<G->e;k++){
cout<<"輸入邊(i,j)和權重w"<<endl;
cin>>i>>j>>w;
G->edge[i][j]=w;//輸入邊(i,j)和權重w
G->edge[j][i]=w;
}
}
void wenjianCreateGraph(mygraph *G){// 從檔案創建鄰接矩陣
fstream in;
in.open("tu1.txt");
int i,j,k,w;
in>>G->n>>G->e;//輸入頂點個數和邊的個數;
for(i=0;i<G->n;i++){
G->vertex[i]=i;
}
for(i=0;i<G->n ;i++)
for(j=0;j<G->n ;j++)
G->edge[i][j]=0;//初始化鄰接矩陣
for(k=0;k<G->e;k++){
in>>i>>j>>w;
G->edge[i][j]=w;//輸入邊(i,j)和權重w
G->edge[j][i]=w;
}
in.close();
}
演算法時間復雜度:O(n^2+n+2e )//n個頂點初始化,n^2個邊的初始化,輸入無向圖的2e條邊
演算法空間復雜度:O(n^2+n )//存盤n個頂點及n^2個邊值資訊
void shuchugraph(mygraph *t){//列印鄰接矩陣的資訊
for(int i=0;i<t->n ;i++){
cout<<"\t"<<i;
}
cout<<endl;
for(int i=0;i<t->n ;i++){
cout<<i<<"\t";
for(int j=0;j<t->n ;j++){
if(t->edge[i][j]==0){
cout<<"∞\t";
}
else
cout<<t->edge[i][j]<<"\t";
}
cout<<endl;
}
}

1.1.3 圖的鄰接表存盤
圖的鄰接表存盤,就是對于每個頂點v來說,和這個頂點v相連的所有鄰接點構成一個線性表,
我們需要把頂點分成兩類,一類是主頂點,一類是和頂點鏈接的邊頂點,
對于每個主頂點,我們需要存盤以下資訊:
- 有一個指標域,指向的是和頂點v鄰接的第一個頂點,
- 頂點的下標,指明了頂點的編號
- 一個標記布爾型別的值,標記這個頂點是否被訪問過(用于圖的遍歷)
typedef struct {//頂點表節點
int dingdian;//頂點表節點
bool zouguo;//標記節點是否被訪問過
Edgenode *firstedge;//邊鏈表頭指標
}Vertex;
對于每一個邊頂點,我們需要存盤以下資訊,
- 頂點下標
- 和主頂點鄰接的邊的權重
- 指向下一邊表節點的指標
typedef struct node{//邊表節點
int xiabiao;//頂點下標
data cost;/和主頂點鄰接的邊的權重
struct node *next;//指向下一邊表節點的指標
}Edgenode;
那么整個圖就可以用一個主頂點陣列來表示,同時需要指明圖的邊的個數和頂點的個數,
typedef struct {//圖的鄰接表
Vertex vexlist[max];//頂點陣列
int n,e;//頂點個數和邊的個數
}adjgraph;
看一個例子

1.1.4 使用圖的鄰接表建立一個圖
輸入頂點個數和邊的個數
初始化頂點鄰接表
根據給定的鄰接頂點i和j,創建邊頂點p和q分別存盤頂點i和j的資訊,讓主頂點i指向q,且讓主頂點j指向邊頂點q,
void CreateadjGraph(adjgraph *G){// 創建鄰接表
int i,k,j;
data weight;
cout<<"請輸入頂點個數和邊的個數"<<endl;
cin>>G->n >>G->e ;
for(i=0;i<G->n;i++){
G->vexlist[i].dingdian=i;
G->vexlist[i].firstedge=NULL;//初始化鄰接表
}
for(k=0;k<G->e ;k++){
cout<<"輸入邊(i,j)和權重w"<<endl;
cin>>i>>j>>weight;
Edgenode* p=new Edgenode;
p->xiabiao =j;p->cost =weight;
p->next =G->vexlist[i].firstedge ;
G->vexlist[i].firstedge =p;
Edgenode* q=new Edgenode;
q->xiabiao =i;q->cost =weight;
q->next =G->vexlist[j].firstedge ;
G->vexlist[j].firstedge =q;
}
}
void wenjianCreateadjGraph(adjgraph *G){// 從檔案創建鄰接表
fstream in;
in.open("tu2.txt");
int i,k,j;
data weight;
in>>G->n >>G->e ;
for(i=0;i<G->n;i++){
G->vexlist[i].dingdian=i;
G->vexlist[i].firstedge=NULL;//初始化鄰接表
}
for(k=0;k<G->e ;k++){
in>>i>>j>>weight;
Edgenode* p=new Edgenode;
p->xiabiao =j;p->cost =weight;
p->next =G->vexlist[i].firstedge ;
G->vexlist[i].firstedge =p;
Edgenode* q=new Edgenode;
q->xiabiao =i;q->cost =weight;
q->next =G->vexlist[j].firstedge ;
G->vexlist[j].firstedge =q;
}
in.close();
}
void shuchuadjgraph(adjgraph *t){//列印鄰接表
for(int i=0;i<t->n ;i++){
cout<<i<<":\t";
Edgenode *p;
p=t->vexlist[i].firstedge;
while(p!=NULL){
cout<<p->xiabiao<<"\t";
p=p->next ;
}
cout<<endl;
}
}
1.2. 圖的鄰接矩陣和圖的鄰接表兩種存盤結構的轉換
1.2.1 圖的鄰接矩陣轉向圖的鄰接表
- 思路很簡單,先初始化鄰接矩陣,根據頂點個數和邊的個數,
- 然后遍歷鄰接矩陣,如果A[i][j] 不等于0 , 則表示頂點i 和 頂點j
之間鄰接,邊權重為A[i][j],然后用之前講過的鄰接表創建圖的方法來創建鄰接表,
void jzhuanhuanb(mygraph *G,adjgraph *T){//將鄰接矩陣G轉化為鄰接表T
T->n=G->n ;//初始化鄰接表
T->e =G->e;//初始化鄰接表
for(int i=0;i<G->n;i++ ){//初始化鄰接表
T->vexlist[i].dingdian =i;
T->vexlist[i].firstedge=NULL;
}
for(int j=0;j<G->n;j++){
for(int k=0;k<G->n ;k++){
if(G->edge[j][k]!=0){
Edgenode *p=new Edgenode;
p->xiabiao=G->vertex[k];//頂點賦值
p->cost =G->edge[j][k]; //邊權重賦值
p->next =T->vexlist[j].firstedge ;
T->vexlist[j].firstedge=p;
}
}
}
}

1.2.2 圖的鄰接表轉向圖的鄰接矩陣
思路同上,先初始化,然后遍歷每個頂點的鄰接頂點,然后給鄰接矩陣賦值即可,
void bzhuanhuanj(mygraph *G,adjgraph *T){//將鄰接表轉換為鄰接矩陣
Edgenode *p;
G->n =T->n;//初始化鄰接矩陣
G->e =T->e ;//初始化鄰接矩陣
int i,j;
for(i=0;i<G->n;i++){
for( j=0;j<G->n;j++){
G->edge[i][j]=0;//初始化鄰接矩陣
}
}
for(int k=0;k<G->n;k++){
p=T->vexlist[k].firstedge ;
while(p!=NULL){
G->edge[k][p->xiabiao]=p->cost;//將邊權重賦值
p=p->next ;
}
}
}

1.3 圖的深度優先遍歷
1.3.1 鄰接矩陣表示的圖的深度優先遍歷
1.3.1.1 遞回
思路:借助一個標記陣列,標記了每個頂點是否被訪問過,
- 首先對每個頂點標記為未訪問過,
- 然后對于頂點v,如果頂點v沒有被訪問過,則訪問這個頂點,并且把這個頂點標記為訪問過,然后遞回的訪問這個頂點鄰接的頂點,
- 訪問完頂點v的所有鄰接的節點后,把森林的個數加1,然后再去訪問沒有訪問過的頂點k,再轉2.
void DFSX(mygraph *G,int x){//輔助深度遞回 (通過鄰接矩陣)
visited[x]=true;
cout<<G->vertex[x]<<" ";
for(int j=0;j<G->n;j++){
if(!visited[j]&&G->edge[x][j]!=0)
DFSX(G,G->vertex[j]);
}
}
void diguishen(mygraph *G){ //鄰接矩陣深度遞回演算法
for(int i=0;i<G->n;i++){//將每個節點標記為未訪問過
visited[i]=false;
}
int count=1;//森林個數
for(int i=0;i<G->n;i++){
if(!visited[i]){
cout<<"森林"<<count++<<":";
DFSX(G,i);
cout<<endl;
}
}
}
1.3.1.2 非遞回
一般對于所有的遞回演算法來說,要把它變成非遞回演算法,則要利用堆疊,
演算法原理:
- 首先對每個頂點標記為未訪問過,
- 然后遍歷所有的頂點,對于頂點v,如果頂點v沒有被訪問過,則訪問這個頂點,并且把這個頂點標記為訪問過,并且把這個頂點壓入堆疊中,
- 當堆疊不為空時,取堆疊頂頂點t,然后訪問所有和頂點t鄰接的頂點x,如果頂點x沒有被訪問過,則訪問這個頂點,并且把這個頂點標記為訪問過,并且把這個頂點x壓入堆疊中,然后再去訪問頂點x鄰接的所有頂點,
- 考察完頂點v所鄰接的所有頂點之后,再去訪問沒有訪問過的頂點,
void feidiguishen2(mygraph *G){ //鄰接矩陣深度非遞回演算法
for(int l=0;l<G->n ;l++)//將每個節點標記為未訪問過
visited[l]=false;
stack<int>s;
int count=1;//森林個數
for(int j=0;j<G->n;j++){
if(!visited[j]){
cout<<"森林"<<count++<<":";
visited[j]=true;
s.push(j);
cout<<G->vertex[j]<<" ";
while(!s.empty() ){
int t = s.top();
bool flag = false;
for(int k=0;k<G->n ;k++){
if(G->edge[t][k]!=0){
if(!visited[k]){
visited[k]=true;
cout<<G->vertex[k]<<" ";
s.push(k);
flag = true;
break ;
}
}
}
if (flag == false )
s.pop() ;
}
cout<<endl;
}
}
}
1.3.2 鄰接表表示的圖的深度優先遍歷
思路一樣,就是代碼不一樣
1.3.2.1 遞回
void DFSX2(adjgraph *G,int x){//鄰接表深度遞回輔助演算法
Edgenode *p;
visited[x]=true;//標記為訪問過
cout<<G->vexlist[x].dingdian<<" ";
p=G->vexlist[x].firstedge;
while(p){
if(!visited[p->xiabiao])
DFSX2(G,p->xiabiao);
p=p->next ;
}
}
void diguishen2(adjgraph *G){//鄰接表深度遞回演算法
for(int i=0;i<G->n;i++){//將每個節點標記為未訪問過
visited[i]=false;
}
int count=1;
for(int i=0;i<G->n;i++){//遍歷每一個節點
if(!visited[i]){
cout<<"森林"<<count++<<":";
DFSX2(G,i);
cout<<endl;
}
}
}
1.3.2.2 非遞回
void feidiguishen(adjgraph *G){//鄰接表深度非遞回演算法
stack<int>s;
Edgenode *p;
for(int j=0;j<G->n;j++){//將每個節點標記為未訪問過
visited[j]=false; }
int count=1;//森林數目
for(int i=0;i<G->n;i++){//遍歷每個節點
if(!visited[i]){
cout<<"森林"<<count<<":";
visited[i]=true;
s.push(i);
cout<<G->vexlist[i].dingdian<<" ";
p=G->vexlist[i].firstedge;
while(!s.empty()){
p=G->vexlist[s.top()].firstedge ;
bool flag = false;
while(p){
if(!visited[p->xiabiao ]){
visited[p->xiabiao ]=true;
cout<<G->vexlist[p->xiabiao].dingdian<<" ";//輸出該節點
s.push(p->xiabiao );
p=G->vexlist[p->xiabiao].firstedge ;
flag = true;
break;
}
else{
p=p->next ;
}
}
if(flag == false){
s.pop() ;
}
}
cout<<endl;
count++;
}
}
}
1.4圖的廣度優先遍歷
廣度優先遍歷和我們之前將的二叉樹的層序遍歷幾乎一樣,有一個不同的地方就是圖不一定是連通的,可能有幾個森林,而二叉樹一定是連通的,
思路:借助佇列來實作,
- 首先對每個頂點標記為未訪問過,
- 然后遍歷所有的頂點,對于頂點v,如果頂點v沒有被訪問過,則訪問這個頂點,并且把這個頂點標記為訪問過,并且把這個頂點壓入佇列中,
- 當佇列不為空時,取隊首頂點t,然后訪問所有和頂點t鄰接的頂點x,如果頂點x沒有被訪問過,則訪問這個頂點,并且把這個頂點標記為訪問過,把這個頂點x壓入佇列中,每一次訪問頂點就從隊首取出一個頂點即可,
- 當佇列為空時,再去考察其他沒有訪問過的頂點,
1.4.1 鄰接矩陣表示的圖的廣度優先遍歷
void guangdu2(mygraph *G) {//鄰接矩陣廣度優先遍歷
for(int j=0;j<G->n;j++){
visited[j]=false;
}
queue<int>s;
int count=1;
for(int i=0;i<G->n;i++){
if(!visited[i]){
cout<<"森林"<<count++<<":";
visited[i]=true;
cout<<G->vertex[i]<<" ";
s.push(G->vertex[i]);
while(!s.empty() ){
int k=s.front() ;
s.pop() ;
for(int j=0;j<G->n;j++){
if(G->edge [k][j]!=0){
if(!visited[j]){
visited[j]=true;
cout<<G->vertex[j]<<" ";
s.push(G->vertex[j]);
}
}
}
}
cout<<endl;
}
}
}
1.4.2鄰接表表示的圖的廣度優先遍歷
void guangdu(adjgraph *G){//鄰接表廣度優先遍歷
for(int j=0;j<G->n;j++){
visited[j]=false;
}
queue<int>s;
Edgenode *p;
int count=1;
for(int i=0;i<G->n;i++){
if(!visited[i]){
cout<<"森林"<<count++<<":";
visited[i]=true;
cout<<G->vexlist[i].dingdian<<" ";
s.push(G->vexlist[i].dingdian);
while(!s.empty() ){
p=G->vexlist[s.front()].firstedge;
s.pop() ;
while(p!=NULL){
if(!visited[p->xiabiao]){
visited[p->xiabiao]=true;
cout<<G->vexlist[p->xiabiao].dingdian<<" ";
s.push(p->xiabiao);
}
p=p->next;
}
}
cout<<endl;
}
}
}
1.5主程式
void zrh(){
int x=0;//用來判斷當前的無向圖是用鄰接矩陣還是鄰接表來表示的,0代表鄰接矩陣,1代表鄰接表
cout<<"-----------------------------------------------------------------"<<endl;
cout<<"0.退出 |"<<endl;
cout<<"1.通過從檔案讀入創建一個無向圖的鄰接矩陣 |"<<endl;
cout<<"2.通過從檔案讀入創建一個無向圖的鄰接表 |"<<endl;
cout<<"3.通過輸入創建一個無向圖的鄰接矩陣 |"<<endl;
cout<<"4.通過輸入創建一個無向圖的鄰接表 |"<<endl;
cout<<"5.顯示鄰接矩陣 |"<<endl;
cout<<"6.顯示鄰接表 |"<<endl;
cout<<"7.將鄰接矩陣轉化為鄰接表并顯示 |"<<endl;
cout<<"8.將鄰接表轉化為鄰接矩陣并顯示 |"<<endl;
cout<<"9.深度優先遞回遍歷無向圖 |"<<endl;
cout<<"10.深度優先非遞回遍歷無向圖 |"<<endl;
cout<<"11.廣度優先遍歷無向圖 |"<<endl;
cout<<"-----------------------------------------------------------------"<<endl;
mygraph g;//創建無向圖 的鄰接矩陣
adjgraph t;//創建無向圖的鄰接表
int n;
cin>>n;
while(n){
switch(n){
case 1:
wenjianCreateGraph(&g);
x=0;
break;
case 2:
wenjianCreateadjGraph(&t);
x=1;
break;
case 3:
CreateGraph(&g);
x=0;
break;
case 4:
CreateadjGraph(&t);
x=1;
break;
case 5:
shuchugraph(&g);
x=0;
break;
case 6:
shuchuadjgraph(&t);
x=1;
break;
case 7:
adjgraph t1;
cout<<"鄰接矩陣為:"<<endl;
shuchugraph(&g);
cout<<"轉化為鄰接表后為:"<<endl ;
jzhuanhuanb(&g,&t1);
shuchuadjgraph(&t1);
break;
case 8:
mygraph t2;
cout<<"鄰接表為:"<<endl;
shuchuadjgraph(&t);
cout<<"轉化為鄰接矩陣后為:"<<endl ;
bzhuanhuanj(&t2,&t);
shuchugraph(&t2);
break;
case 9:
if(x==1){
diguishen2(&t);
}
else{
diguishen(&g);
}
break;
case 10:
if(x==1){
feidiguishen(&t);
}
else{
feidiguishen2(&g);
}
break;
case 11:
if(x==0){
guangdu2(&g);
}
else{
guangdu(&t);
}
break;
default :
n=0;
break;
}
cout<<"-----------------------------------------------------------------"<<endl;
cout<<"0.退出 |"<<endl;
cout<<"1.通過從檔案讀入創建一個無向圖的鄰接矩陣 |"<<endl;
cout<<"2.通過從檔案讀入創建一個無向圖的鄰接表 |"<<endl;
cout<<"3.通過輸入創建一個無向圖的鄰接矩陣 |"<<endl;
cout<<"4.通過輸入創建一個無向圖的鄰接表 |"<<endl;
cout<<"5.顯示鄰接矩陣 |"<<endl;
cout<<"6.顯示鄰接表 |"<<endl;
cout<<"7.將鄰接矩陣轉化為鄰接表并顯示 |"<<endl;
cout<<"8.將鄰接表轉化為鄰接矩陣并顯示 |"<<endl;
cout<<"9.深度優先遞回遍歷無向圖 |"<<endl;
cout<<"10.深度優先非遞回遍歷無向圖 |"<<endl;
cout<<"11.廣度優先遍歷無向圖 |"<<endl;
cout<<"-----------------------------------------------------------------"<<endl;
if(x==0)
cout<<"當前無向圖為鄰接矩陣表示法,請根據提示選擇正確的操作:"<<endl;
else
cout<<"當前無向圖為鄰接表表示法,請根據提示選擇正確的操作:"<<endl;
if(n!=0)
cin>>n;
}
}
1.6結果分析
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/202232.html
標籤:其他
