哈工大数据结构实验三——图形结构及其应用

   日期:2020-11-06     浏览:170    评论:0    
核心提示:数据结构实验三目录0.实验要求1.实验思路1.1实现图的存储1.1.1 图的邻接矩阵存储1.1.2 使用图的邻接矩阵建立一个图1.1.3 图的邻接表存储1.1.4 使用图的邻接表建立一个图0.实验要求1.实验思路1.1实现图的存储1.1.1 图的邻接矩阵存储图的邻接矩阵存储就是,把图看成是一个二维矩阵,矩阵A[i][j]的值就表示节点i和节点j之间的边的权重,如果顶点i和顶点j之间没有边,则设置A[i][j] = 正无穷大。因此,用邻接矩阵的形式存储图的结构,我们需要以下三个信息邻接矩阵

数据结构实验三目录

  • 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] = 正无穷大。
因此,用邻接矩阵的形式存储图的结构,我们需要以下三个信息

  1. 邻接矩阵
  2. 所有的顶点的信息
  3. 图的顶点个数和边的个数
#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 使用图的邻接矩阵建立一个图

  1. 指定图的顶点的个数和边的个数
  2. 初始化邻接表和邻接矩阵
  3. 根据给定的顶点和顶点的权重改变邻接矩阵的值。 注意:对于无向图来说,邻接矩阵是对称的,所以如果顶点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相连的所有邻接点构成一个线性表。

我们需要把顶点分成两类,一类是主顶点,一类是和顶点链接的边顶点。

对于每个主顶点,我们需要存储以下信息:

  1. 有一个指针域,指向的是和顶点v邻接的第一个顶点。
  2. 顶点的下标,指明了顶点的编号
  3. 一个标记布尔类型的值,标记这个顶点是否被访问过(用于图的遍历)
typedef struct { //顶点表节点 
   int dingdian;//顶点表节点
   bool zouguo;//标记节点是否被访问过 
   Edgenode *firstedge;//边链表头指针 
}Vertex;

对于每一个边顶点,我们需要存储以下信息。

  1. 顶点下标
  2. 和主顶点邻接的边的权重
  3. 指向下一边表节点的指针
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 图的邻接矩阵转向图的邻接表

  1. 思路很简单,先初始化邻接矩阵,根据顶点个数和边的个数。
  2. 然后遍历邻接矩阵,如果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 递归

思路:借助一个标记数组,标记了每个顶点是否被访问过。

  1. 首先对每个顶点标记为未访问过。
  2. 然后对于顶点v,如果顶点v没有被访问过,则访问这个顶点,并且把这个顶点标记为访问过,然后递归的访问这个顶点邻接的顶点。
  3. 访问完顶点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 非递归

一般对于所有的递归算法来说,要把它变成非递归算法,则要利用栈。
算法原理:

  1. 首先对每个顶点标记为未访问过。
  2. 然后遍历所有的顶点,对于顶点v,如果顶点v没有被访问过,则访问这个顶点,并且把这个顶点标记为访问过,并且把这个顶点压入栈中。
  3. 当栈不为空时,取栈顶顶点t,然后访问所有和顶点t邻接的顶点x。如果顶点x没有被访问过,则访问这个顶点,并且把这个顶点标记为访问过,并且把这个顶点x压入栈中。然后再去访问顶点x邻接的所有顶点。
  4. 考察完顶点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图的广度优先遍历

广度优先遍历和我们之前将的二叉树的层序遍历几乎一样。有一个不同的地方就是图不一定是连通的,可能有几个森林,而二叉树一定是连通的。
思路:借助队列来实现。

  1. 首先对每个顶点标记为未访问过。
  2. 然后遍历所有的顶点,对于顶点v,如果顶点v没有被访问过,则访问这个顶点,并且把这个顶点标记为访问过,并且把这个顶点压入队列中。
  3. 当队列不为空时,取队首顶点t,然后访问所有和顶点t邻接的顶点x。如果顶点x没有被访问过,则访问这个顶点,并且把这个顶点标记为访问过,把这个顶点x压入队列中。每一次访问顶点就从队首取出一个顶点即可。
  4. 当队列为空时,再去考察其他没有访问过的顶点。

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结果分析

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服