数据结构实验9:图的相关操作(待填坑)

实验9

姓名: 学号:班级: 

8.1 实验目的

(1) 掌握图的基本概念。

(2) 掌握图的存储结构的设计与实现,基本运算的实现。

(3) 熟练掌握图的两种遍历算法、遍历生成树及遍历算法的应用。

8.2 实验任务

分别设计图(网)的邻接矩阵、邻接表存储结构,编写算法实现下列问题的求解。、

数据要求:可利用8.3中的数据,也可自行编写数据。

1.打印出图(网)的两种遍历序列。

2.求给定图中的边(或弧)的数目。

3.对给定的图G及出发点v0,设计算法从V0出发深度优先遍历图G,并构造出相应的生成树或生成森林。

4.对给定的图G及出发点v0,设计算法从V0出发广度优先遍历图G,并构造出相应的生成树或生成森林。

8.3 实验说明

这里介绍一种从文本文件读入图的数据创建图的方法,这样我们可以按照指定的格式,先从容地准备好数据,然后由程序自动读入数据来创建图。

createGrpAdjMatrix.h,文件创建邻接矩阵表示的图。

createGrpAdjLinkedList.h,文件创建邻接表表示的图。

(以下给出的图的创建方法仅供参考,实验者可自行设计其它创建方法)

1. 数据文件格式设计

这里数据用文本文件保存,文件扩展名可自行指定,比如g8.grp,只要数据按文本文件格式读写即可。下面给出一种数据文件格式,其实读者可以自行设计图的数据文件格式。

①标识行1:Graph

标识这是一个图的数据文件,这一行也可以不要。

②标识行2:UDG、或UDN、或DG、或DN

这一行用来标识此图是无向图(UDG)、无向网(UDN)、有向图(DG)、还是有向网(DN)。

③顶点行

这一行将图中所有顶点列出,顶点之间用空格进行分割。这些顶点数据读出后存放到图的顶点数组中。

例如,图6-21(a)所示的图的顶点行数据为:a b c d。

图的各种算法都是用顶点的编号来引用顶点的,所以这一行顶点的排列顺序是很重要的,顶点的排列顺序决定了顶点的编号。比如上例中,顶点a、b、c、d对应的编号就为1、2、3、4。

④边数据行

一条边一行,边的2个顶点之间用空格分割。如果是网,每一行再加边的权值,也以空格分割。如果是无向图和无向网,每条边会重复一次。

例如图6-18(a)无向图的边的数据为:

a b

a c

a d

b a

b c

c a

c b

c d

d a

d c

图6-21(a)无向网边的数据为:

a b 4

a c 5

a d 3

b a 4

b c 2

c a 5

c b 2

c d 6

d a 3

d c 6

⑤其它行

如果程序强大一点,还可以在文件中加注释行,允许出现空行等,当然这是非必须的。

举一个完整的图的数据文件的例子,对图6-18(a)的无向图,完整的数据文件如下:

//文件可以加注释行,注释以“//”开始

//Graph为图标志,否则判定格式不对

//标志行后,第一行为图的类型。UDG--无向图;UDN--无向网;DG--有向图;DN--有向网

//标志行后,第二行为顶点元素

//顶点行以下图的边或弧。用顶点表示,第一列为起始顶点;第二列为邻接点;在网中再增加一列表示权值。

//本图具有4个顶点5条边

//下一行为图的标识行

                     Graph

//图的类型标识,此为无向图

UDG

//顶点元素数据

a b c d

//以下为边的数据,共10行数据,表示5条边

a b

a c

a d

b a

b c

c a

c b

c d

d a

d c

文件名不妨叫做Gudg4.grp。

再举一个有向网的例子,对图6-22所示的有向网,完整的数据文件如下:

图1 一个有向网实例

//标识为图数据

                     Graph

//标识有向网

DN

//顶点数据

a b c d e f g h i j

//以下为边数据,共15条边

a b 2

a d 20

b e 1

c a 3

d c 8

d f 6

d g 4

e c 7

e h 3

f c 1

g h 1

h f 2

h j 2

i g 2

j i 1

不妨设文件名为Gdn10.grp

2. 从数据文件创建邻接矩阵表示的图

指定图的数据文件名,然后逐行读出数据并处理,自动创建邻接矩阵表示的图。本程序可以自动处理注释行和空行,程序实现如下:

  1 //****************************文件创建图*****************************//
  2 //* 函数功能:从文本文件创建邻接矩阵表示的图                        *//
  3 //* 入口参数  char fileName[],文件名                               *//
  4 //* 出口参数:Graph &G,即创建的图                                  *//
  5 //* 返回值:bool,true创建成功;false创建失败                    *//
  6 //* 函数名:CreateGraphFromFile(char fileName[], Graph &G)        *//
  7 //*******************************************************************//
  8 int CreateGraphFromFile(char fileName[], Graph &G)
  9 {
 10     FILE* pFile;          //定义文件指针
 11     char str[1000];         //存放读出一行文本的字符串
 12     char strTemp[10];      //判断是否注释行
 13     cellType eWeight;      //边的信息,常为边的权值
 14     GraphKind graphType;  //图类型枚举变量
 15     pFile=fopen(fileName,"r");
 16     if(!pFile)
 17     {
 18         printf("错误:文件%s打开失败。\n",fileName);
 19         return false;
 20     }
 21     while(fgets(str,1000,pFile)!=NULL)
 22     {
 23         strLTrim(str);     //删除字符串左边空格,这是一个自定义的函数
 24         if (str[0]=='\n')    //空行,继续读取下一行
 25             continue;
 26         strncpy(strTemp,str,2);
 27         if(strstr(strTemp,"//")!=NULL)  //跳过注释行
 28             continue;
 29         else                       //非注释行、非空行,跳出循环
 30             break;
 31     }
 32     //循环结束,str中应该已经是图的标识Graph,判断标识是否正确
 33     if(strstr(str,"Graph")==NULL)
 34     {
 35         printf("错误:打开的文件格式错误!\n");
 36         fclose(pFile); //关闭文件
 37         return false;
 38     }
 39     //读取图的类型,跳过空行
 40     while(fgets(str,1000,pFile)!=NULL)
 41     {
 42         strLTrim(str);    //删除字符串左边空格,这是一个自定义函数
 43         if (str[0]=='\n')   //空行,继续读取下一行
 44             continue;
 45         strncpy(strTemp,str,2);
 46         if(strstr(strTemp,"//")!=NULL)  //注释行,跳过,继续读取下一行
 47             continue;        
 48         else                       //非空行,也非注释行,即图的类型标识
 49             break;
 50     }
 51     //设置图的类型
 52     if(strstr(str,"UDG"))
 53         graphType=UDG;    //无向图
 54     else if(strstr(str,"UDN"))
 55         graphType=UDN;    //无向网
 56     else if(strstr(str,"DG"))
 57         graphType=DG;     //有向图
 58     else if(strstr(str,"DN"))
 59         graphType=DN;     //有向网
 60     else
 61     {
 62         printf("错误:读取图的类型标记失败!\n");
 63         fclose(pFile);       //关闭文件
 64         return false;
 65     }
 66     //读取顶点行数据到str。跳过空行
 67     while(fgets(str,1000,pFile)!=NULL)
 68     {
 69         strLTrim(str);      //删除字符串左边空格,这是一个自定义函数
 70         if (str[0]=='\n')     //空行,继续读取下一行
 71             continue;
 72         strncpy(strTemp,str,2);
 73         if(strstr(strTemp,"//")!=NULL)  //注释行,跳过,继续读取下一行
 74             continue;        
 75         else                       //非空行,也非注释行,即图的顶点元素行
 76             break;
 77     }
 78 
 79     //顶点数据放入图的顶点数组    
 80     char* token=strtok(str," ");
 81     int nNum=0;    
 82     while(token!=NULL)
 83     {
 84         G.Data[nNum]=*token; 
 85       token = strtok( NULL, " ");
 86         nNum++;
 87     }
 88     //图的邻接矩阵初始化
 89     int nRow=0;    //矩阵行下标
 90     int nCol=0;     //矩阵列下标
 91     if(graphType==UDG || graphType==DG)
 92     {
 93         for(nRow=0;nRow<nNum;nRow++)
 94             for(nCol=0;nCol<nNum;nCol++)
 95                 G.AdjMatrix[nRow][nCol]=0;
 96     }
 97     else
 98     {
 99         for(nRow=0;nRow<nNum;nRow++)
100             for(nCol=0;nCol<nNum;nCol++)
101                 G.AdjMatrix[nRow][nCol]=INF;  //INF表示无穷大
102     }
103         //循环读取边的数据到邻接矩阵
104     int edgeNum=0;         //边的数量
105     elementType Nf, Ns;     //边或弧的2个相邻顶点
106     while(fgets(str,1000,pFile)!=NULL)
107     {
108         strLTrim(str);       //删除字符串左边空格,这是一个自定义函数
109         if (str[0]=='\n')      //空行,继续读取下一行
110             continue;
111         strncpy(strTemp,str,2);
112         if(strstr(strTemp,"//")!=NULL)  //注释行,跳过,继续读取下一行
113             continue;
114         char* token=strtok(str," ");  //以空格为分隔符,分割一行数据,写入邻接矩阵
115         if(token==NULL)         //分割为空串,失败退出
116         {
117             printf("错误:读取图的边数据失败!\n");
118             fclose(pFile);         //关闭文件
119             return false;
120         }
121         Nf=*token;               //获取边的第一个顶点
122         token = strtok( NULL, " ");   //读取下一个子串,即第二个顶点
123         if(token==NULL)          //分割为空串,失败退出
124         {
125             printf("错误:读取图的边数据失败!\n");
126             fclose(pFile);          //关闭文件
127             return false;
128         }
129         Ns=*token;  //获取边的第二个顶点
130                  //从第一个顶点获取行号        
131         for(nRow=0;nRow<nNum;nRow++)
132         {
133             if(G.Data[nRow]==Nf)  //从顶点列表找到第一个顶点的编号
134                 break;
135         }
136                   //从第二个顶点获取列号
137         for(nCol=0;nCol<nNum;nCol++)
138         {
139             if(G.Data[nCol]==Ns)  //从顶点列表找到第二个顶点的编号
140                 break;
141         }
142         //如果为网,读取权值
143         if(graphType==UDN || graphType==DN)
144         {                //读取下一个子串,即边的附加信息,常为边的权重
145             token = strtok( NULL, " ");  
146             if(token==NULL)    //分割为空串,失败退出
147             {
148                 printf("错误:读取图的边数据失败!\n");
149                 fclose(pFile);    //关闭文件
150                 return false;
151             }
152             eWeight=atoi(token);  //取得边的附加信息
153         }
154         if(graphType==UDN || graphType==DN)  
155             G.AdjMatrix[nRow][nCol]=eWeight;
156 //如果为网,邻接矩阵中对应的边设置权值,否则置为1
157         else
158             G.AdjMatrix[nRow][nCol]=1;  
159         edgeNum++;   //边数加1
160     }
161   G.VerNum=nNum;           //图的顶点数
162     if(graphType==UDG || graphType==UDN)
163         G.ArcNum=edgeNum / 2;  //无向图或网的边数等于统计的数字除2  
164     else
165         G.ArcNum=edgeNum;
166     G.gKind=graphType;         //图的类型
167     fclose(pFile);               //关闭文件
168     return true;
169 }

3. 从数据文件创建邻接表表示的图

程序实现如下:

  1 //****************************文件创建图*****************************//
  2 //* 函数功能:从文本文件创建邻接表表示的图                          *//
  3 //* 入口参数  char fileName[],文件名                               *//
  4 //* 出口参数:Graph &G,即创建的图                                  *//
  5 //* 返回值:bool,true创建成功;false创建失败                    *//
  6 //* 函数名:CreateGraphFromFile(char fileName[], Graph &G)        *//
  7 //* 备注:本函数使用的数据文件格式以边(顶点对)为基本数据          *//
  8 //*******************************************************************//
  9 int CreateGraphFromFile(char fileName[], Graph &G)
 10 {
 11     FILE* pFile;         //定义文件指针
 12     char str[1000];        //存放读出一行文本的字符串
 13     char strTemp[10];      //判断是否注释行
 14     char* ss; 
 15 int i=0, j=0;
 16     int edgeNum=0;        //边的数量
 17     eInfoType eWeight;     //边的信息,常为边的权值
 18     GraphKind graphType;  //图类型枚举变量
 19     pFile=fopen(fileName,"r");
 20     if(!pFile)
 21     {
 22         printf("错误:文件%s打开失败。\n",fileName);
 23         return false;
 24     }
 25         while(fgets(str,1000,pFile)!=NULL)  //跳过空行和注释行
 26     {
 27         strLTrim(str);     //删除字符串左边空格,这是一个自定义函数
 28         if (str[0]=='\n')    //空行,继续读取下一行
 29             continue;
 30         strncpy(strTemp,str,2);
 31         if(strstr(strTemp,"//")!=NULL)    //跳过注释行
 32             continue;
 33         else                          //非注释行、非空行,跳出循环
 34             break;
 35     }
 36   //循环结束,str中应该已经是图的标识Graph,判断标识是否正确
 37     if(strstr(str,"Graph")==NULL)
 38     {
 39         printf("错误:打开的文件格式错误!\n");
 40         fclose(pFile);                //关闭文件
 41         return false;
 42     }
 43     //读取图的类型,跳过空行及注释行
 44     while(fgets(str,1000,pFile)!=NULL)
 45     {
 46         strLTrim(str);     //删除字符串左边空格,这是一个自定义函数
 47         if (str[0]=='\n')    //空行,继续读取下一行
 48             continue;
 49         strncpy(strTemp,str,2);
 50         if(strstr(strTemp,"//")!=NULL)  //注释行,跳过,继续读取下一行
 51             continue;        
 52         else                       //非空行,也非注释行,即图的类型标识
 53             break;
 54     }
 55   //设置图的类型
 56     if(strstr(str,"UDG"))
 57         graphType=UDG;    //无向图
 58     else if(strstr(str,"UDN"))
 59         graphType=UDN;    //无向网
 60     else if(strstr(str,"DG"))
 61         graphType=DG;     //有向图
 62     else if(strstr(str,"DN"))
 63         graphType=DN;     //有向网
 64     else
 65     {
 66         printf("错误:读取图的类型标记失败!\n");
 67         fclose(pFile);       //关闭文件
 68         return false;
 69     }
 70     //读取顶点数据到str。跳过空行
 71     while(fgets(str,1000,pFile)!=NULL)
 72     {
 73         strLTrim(str);     //删除字符串左边空格,这是一个自定义函数
 74         if (str[0]=='\n')    //空行,继续读取下一行
 75             continue;
 76         strncpy(strTemp,str,2);
 77         if(strstr(strTemp,"//")!=NULL)  //注释行,跳过,继续读取下一行
 78             continue;        
 79         else                       //非空行,也非注释行,即图的顶点元素行
 80             break;
 81     }
 82     //顶点数据放入图的顶点数组        
 83     char* token=strtok(str," ");
 84     int nNum=0;    
 85     while(token!=NULL)
 86     {
 87         G.VerList[nNum].data=*token;
 88         G.VerList[nNum].firstEdge=NULL;
 89 token = strtok( NULL, " ");
 90         nNum++;
 91     }
 92     //循环读取边(顶点对)数据
 93     int nRow=0;          //矩阵行下标
 94     int nCol=0;           //矩阵列下标
 95     EdgeNode* eR;       //边链表尾指针
 96     EdgeNode* p;    
 97     elementType Nf, Ns;   //边或弧的2个相邻顶点
 98     while(fgets(str,1000,pFile)!=NULL)
 99     {
100         strLTrim(str);     //删除字符串左边空格,这是一个自定义函数
101         if (str[0]=='\n')    //空行,继续读取下一行
102             continue;
103         strncpy(strTemp,str,2);
104         if(strstr(strTemp,"//")!=NULL)  //注释行,跳过,继续读取下一行
105             continue;
106         char* token=strtok(str," ");     //以空格为分隔符,分割一行数据
107         if(token==NULL)            //分割为空串,失败退出
108         {
109             printf("错误:读取图的边数据失败!\n");
110             fclose(pFile);            //关闭文件
111             return false;
112         }
113         Nf=*token;                //获取边的第一个顶点
114         token = strtok( NULL, " ");   //读取下一个子串,即第二个顶点
115         if(token==NULL)          //分割为空串,失败退出
116         {
117             printf("错误:读取图的边数据失败!\n");
118             fclose(pFile);          //关闭文件
119             return false;
120         }
121         Ns=*token;    //获取边的第二个顶点
122                    //从第一个顶点获取行号
123         for(nRow=0;nRow<nNum;nRow++)
124         {
125             if(G.VerList[nRow].data==Nf)  //从顶点列表找到第一个顶点的编号
126                 break;
127         }
128                     //从第二个顶点获取列号
129         for(nCol=0;nCol<nNum;nCol++)
130         {
131             if(G.VerList[nCol].data==Ns)  //从顶点列表找到第二个顶点的编号
132                 break;
133         }
134         //如果为网,读取权值
135         if(graphType==UDN || graphType==DN)
136         {            //读取下一个子串,即边的附加信息,常为边的权重
137             token = strtok( NULL, " ");  
138             if(token==NULL)    //分割为空串,失败退出
139             {
140                 printf("错误:读取图的边数据失败!\n");
141                 fclose(pFile);    //关闭文件
142                 return false;
143             }
144             eWeight=atoi(token);  //取得边的附加信息,即权值
145         }
146         eR=G.VerList[nRow].firstEdge;
147         while(eR!=NULL && eR->next!=NULL)
148         {
149             eR=eR->next;        //后移边链表指针,直至尾节点
150         }
151         p=new EdgeNode;        //申请一个边链表结点
152         p->adjVer=nCol+1;       //顶点的编号,从1开始
153         //边的附加信息(权值),对有权图保存权值,无权图为1
154 if(graphType==UDN || graphType==DN) 
155             p->eInfo=eWeight;
156         else
157             p->eInfo=1; 
158         p->next=NULL;
159         if(G.VerList[nRow].firstEdge==NULL)
160         {
161             G.VerList[nRow].firstEdge=p;
162             eR=p;
163         }
164         else
165         {
166             eR->next=p;
167             eR=p;       //新的尾指针                
168         }                
169         edgeNum++;     //边数加1
170     }
171     G.VerNum=nNum;    //图的顶点数
172     if(graphType==UDG || graphType==UDN)
173         G.ArcNum=edgeNum / 2;   //无向图或网的边数等于统计的数字除2  
174     else
175         G.ArcNum=edgeNum;
176     G.gKind=graphType;           //图的类型
177     fclose(pFile);                 //关闭文件
178     return true;
179 }

4. 图的销毁

以邻接矩阵为存储结构的图,因为使用矩阵存储图的数据,不存在销毁(释放内存)问题。但是以邻接表为存储结构的图,由于在创建图的过程中使用malloc()函数或new操作符动态申请了内存,当这个图不再需要时,必须手工释放动态申请的内存,否则造成内存泄漏。下面给出一个销毁邻接表表示的图的程序。

猜你喜欢

转载自www.cnblogs.com/25th-engineer/p/10225101.html