第7次作业
这个作业属于哪个课程 | 数据结构 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/qdu/DS2020/homework/11472 |
这个作业的目标 | <1、掌握图的邻接矩阵和邻接表表示2、掌握图的深度优先和广度优先搜索方法3、理解图的应用方法> |
学号 | 2018204106 |
一、实验目的
1、掌握图的邻接矩阵和邻接表表示
2、掌握图的深度优先和广度优先搜索方法
3、理解图的应用方法
二、实验预习
说明以下概念
1、深度优先搜索遍历:
深度优先搜索是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链的HTML文件) 。在一个HTML文件中,当一个超链被选择后,被链接的HTML文件将执行深度优先搜索,即在搜索其余的超链结果之前必须先完整地搜索单独的一条链。深度优先搜索沿着HTML文件上的超链走到不能再深入为止,然后返回到某一个HTML文件,再继续选择该HTML文件中的其他超链。当不再有其他超链可选择时,说明搜索已经结束。
2、广度优先搜索遍历:
广度优先遍历算法是图的另一种基本遍历算法,其基本思想是尽最大程度辐射能够覆盖的节点,并对其进行访问。
3、拓扑排序:
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
4、最小生成树:
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。
5、最短路径:
用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。
三、实验内容和要求
1、阅读并运行下面程序,根据输入写出运行结果。
#include<stdio.h> #define N 20 #define TRUE 1 #define FALSE 0 int visited[N]; typedef struct /*队列的定义*/ { int data[N]; int front,rear; }queue; typedef struct /*图的邻接矩阵*/ { int vexnum,arcnum; char vexs[N]; int arcs[N][N]; } graph; void createGraph(graph *g); /*建立一个无向图的邻接矩阵*/ void dfs(int i,graph *g); /*从第i个顶点出发深度优先搜索*/ void tdfs(graph *g); /*深度优先搜索整个图*/ void bfs(int k,graph *g); /*从第k个顶点广度优先搜索*/ void tbfs(graph *g); /*广度优先搜索整个图*/ void init_visit(); /*初始化访问标识数组*/ void createGraph(graph *g) /*建立一个无向图的邻接矩阵*/ { int i,j; char v; g->vexnum=0; g->arcnum=0; i=0; printf("输入顶点序列(以#结束):\n"); while((v=getchar())!='#') { g->vexs[i]=v; /*读入顶点信息*/ i++; } g->vexnum=i; /*顶点数目*/ for(i=0;i<g->vexnum;i++) /*邻接矩阵初始化*/ for(j=0;j<g->vexnum;j++) g->arcs[i][j]=0; printf("输入边的信息:\n"); scanf("%d,%d",&i,&j); /*读入边i,j*/ while(i!=-1) /*读入i,j为-1时结束*/ { g->arcs[i][j]=1; g->arcs[j][i]=1; scanf("%d,%d",&i,&j); } } void dfs(int i,graph *g) /*从第i个顶点出发深度优先搜索*/ { int j; printf("%c",g->vexs[i]); visited[i]=TRUE; for(j=0;j<g->vexnum;j++) if((g->arcs[i][j]==1)&&(!visited[j])) dfs(j,g); } void tdfs(graph *g) /*深度优先搜索整个图*/ { int i; printf("\n从顶点%C开始深度优先搜索序列:",g->vexs[0]); for(i=0;i<g->vexnum;i++) if(visited[i]!=TRUE) dfs(i,g); } void bfs(int k,graph *g) /*从第k个顶点广度优先搜索*/ { int i,j; queue qlist,*q; q=&qlist; q->rear=0; q->front=0; printf("%c",g->vexs[k]); visited[k]=TRUE; q->data[q->rear]=k; q->rear=(q->rear+1)%N; while(q->rear!=q->front) { i=q->data[q->front]; q->front=(q->front+1)%N; for(j=0;j<g->vexnum;j++) if((g->arcs[i][j]==1)&&(!visited[j])) { printf("%c",g->vexs[j]); visited[j]=TRUE; q->data[q->rear]=j; q->rear=(q->rear+1)%N; } } } void tbfs(graph *g) /*广度优先搜索整个图*/ { int i; printf("\n从顶点%C开始广度优先搜索序列:",g->vexs[0]); for(i=0;i<g->vexnum;i++) if(visited[i]!=TRUE) bfs(i,g); } void init_visit() /*初始化访问标识数组*/ { int i; for(i=0;i<N;i++) visited[i]=FALSE; } int main() { graph ga; int i,j; createGraph(&ga); printf("无向图的邻接矩阵:\n"); for(i=0;i<ga.vexnum;i++) { for(j=0;j<ga.vexnum;j++) printf("%3d",ga.arcs[i][j]); printf("\n"); } init_visit(); tdfs(&ga); init_visit(); tbfs(&ga); return 0; }
2、阅读并运行下面程序,补充拓扑排序算法。
#include<stdio.h> #include<malloc.h> const int N=20; typedef struct edgenode /*图的邻接表:邻接链表结点*/ { int adjvex; /*顶点序号*/ struct edgenode *next; /*下一个结点的指针*/ } edgenode; typedef struct vnode /*图的邻接表:邻接表*/ { char data; /*顶点信息*/ int ind; /*顶点入度*/ struct edgenode *link; /*指向邻接链表指针*/ } vnode; int createGraph_list(vnode adjlist[],int *p); /*建立有向图的邻接表*/ void topSort(vnode g[],int n); /*拓扑排序*/ int createGraph_list(vnode adjlist[],int *p) /*建立有向图的邻接表*/ { int i,j,n,e; char v; edgenode *s; i=0; n=0; e=0; printf("输入顶点序列(以#结束):\n"); while((v=getchar())!='#') { adjlist[i].data=v; /*读入顶点信息*/ adjlist[i].link=NULL; adjlist[i].ind=0; i++; } n=i; *p=n; /*建立邻接链表*/ printf("\n请输入弧的信息(i=-1结束):i,j:\n"); scanf("%d,%d",&i,&j); while(i!=-1) { s=(struct edgenode*)malloc(sizeof(edgenode)); s->adjvex=j; s->next=adjlist[i].link; adjlist[i].link=s; adjlist[j].ind++; /*顶点j的入度加1*/ e++; scanf("%d,%d",&i,&j); } printf("邻接表:"); for(i=0; i<n; i++) /*输出邻接表*/ { printf("\n%c,%d:",adjlist[i].data,adjlist[i].ind); s=adjlist[i].link; while(s!=NULL) { printf("->%d",s->adjvex); s=s->next; } } return n; } void topSort(vnode g[],int n) /*拓扑排序*/ { printf("输入拓扑排序顶点序列:\n"); int i,j,k,m=0,top=-1; struct edgenode *p; for (i=0; i<=n; i++) //将度为零的顶点入栈 if (g[i].ind==0) { g[i].ind=top; top=i; } while (top!=-1) //栈不为空 { j=top; top=g[top].ind; //出栈 printf("%c",g[j].data); m++; p=g[j].link; while (p) //删除该节点的所有边 { k=p->adjvex; g[k].ind--; if (g[k].ind==0) //将入度为零的点入栈 { g[k].ind=top; top=k; } p=p->next; } } // if (m<n) // printf("该图存在环\n"); } int main() { vnode adjlist[N]; int n,*p; int m; p=&n; m=createGraph_list(adjlist,p); printf("\n"); topSort(adjlist,m-1); return 0; }
3、阅读并运行下面程序。
#define N 20 #define TRUE 1 #define INF 32766 /*邻接矩阵中的无穷大元素*/ #define INFIN 32767 /*比无穷大元素大的数*/ typedef struct { /*图的邻接矩阵*/ int vexnum,arcnum; char vexs[N]; int arcs[N][N]; } graph; void createGraph_w(graph *g,int flag); void prim(graph *g,int u); void dijkstra(graph g,int v); void showprim(); void showdij(); /*建带权图的邻接矩阵,若flag为1则为无向图,flag为0为有向图*/ void createGraph_w(graph *g,int flag) { int i,j,w; char v; g->vexnum=0; g->arcnum=0; i=0; printf("输入顶点序列(以#结束):\n"); while((v=getchar())!='#') { g->vexs[i]=v; /*读入顶点信息*/ i++; } g->vexnum=i; for(i=0;i<6;i++) /*邻接矩阵初始化*/ for(j=0;j<6;j++) g->arcs[i][j]=INF; printf("输入边的信息:\n"); scanf("%d,%d,%d",&i,&j,&w); /*读入边(i,j,w)*/ while(i!=-1) /*读入i为-1时结束*/ { g->arcs[i][j]=w; if(flag==1) g->arcs[j][i]=w; scanf("%d,%d,%d",&i,&j,&w); } } void prim(graph *g,int u)/*出发顶点u*/ { int lowcost[N],closest[N],i,j,k,min; for(i=0;i<g->vexnum;i++) /*求其他顶点到出发顶点u的权*/ { lowcost[i]=g->arcs[u][i]; closest[i]=u; } lowcost[u]=0; for(i=1;i<g->vexnum;i++) /*循环求最小生成树中的各条边*/ { min=INFIN; for(j=0;j<g->vexnum;j++) /*选择得到一条代价最小的边*/ if(lowcost[j]!=0&&lowcost[j]<min) { min=lowcost[j]; k=j; } printf("(%c,%c)--%d\n",g->vexs[closest[k]],g->vexs[k],lowcost[k]); /*输出该边*/ lowcost[k]=0; /*顶点k纳入最小生成树 */ for(j=0;j<g->vexnum;j++) /*求其他顶点到顶点k 的权*/ if(g->arcs[k][j]!=0&&g->arcs[k][j]<lowcost[j]) { lowcost[j]=g->arcs[k][j]; closest[j]=k; } } } void printPath(graph g,int startVex,int EndVex) { int stack[N],top=0; /*堆栈*/ int i,k,j; int flag[N]; /*输出路径顶点标志*/ k=EndVex; for (i=0;i<g.vexnum;i++) flag[i]=0; j=startVex; printf("%c",g.vexs[j]); flag[j]=1; stack[top++]=k; while (top>0) /*找j到k的路径*/ { for (i=0;i<g.vexnum;i++) { if (path[k][i]==1 && flag[i]==0) /*j到k的路径含有i顶点*/ { if (g.arcs[j][i]!=INF ) /*j到i的路径含有中间顶点*/ { printf("-> %c(%d) ",g.vexs[i],g.arcs[j][i]); /*输出j到k的路径的顶点i*/ flag[i]=1; j=i; k=stack[--top]; break; } else { if (i!=k) stack[top++]=i; /*break;*/ } } } } void dijkstra(graph g,int v){ /*dijkstra算法求单源最短路径*/ int path[N][N],dist[N],s[N]; int mindis,i,j,u,k; for(i=0;i<g.vexnum;i++){ dist[i]=g.arcs[v][i]; s[i]=0; for(j=0;j<g.vexnum;j++) path[i][j]=0; if(dist[i]<INF){ path[i][v]=1; path[i][i]=1; } } dist[v]=0; s[v]=1; for(i=0,u=1;i<g.vexnum;i++){ mindis=INFIN; for(j=0;j<g.vexnum;j++) if(s[j]==0) if(dist[j]<mindis){ u=j; mindis=dist[j]; } s[u]=1; for(j=0;j<g.vexnum;j++) if((s[j]==0)&&dist[u]+g.arcs[u][j]<dist[j]){ dist[j]=dist[u]+g.arcs[u][j]; for(k=0;k<g.vexnum;k++) path[j][k]=path[u][k]; path[j][j]=1; } } printf("\n顶点%c->到各顶点的最短路径\n",g.vexs[v]); for(i=0;i<g.vexnum;i++){ printf("\n顶点%c->顶点%c:",g.vexs[v],g.vexs[i]); if(dist[i]==INF||dist[i]==0) printf("无路径"); else{ printf("%d ",dist[i]); printf("经过顶点:"); printPath(g,v,i); /*输出v到i的路径*/ } } } void showprim()/*最小生成树prim算法演示*/ { graph ga; createGraph_w(&ga,1); prim(&ga,0); } void showdij(){ /*dijstra算法演示*/ graph ga; createGraph_w(&ga,0); dijkstra(ga,0); } int main(){ showprim(); /*prim算法演示*/ getchar(); showdij(); /*dijstra算法演示*/ return 0; }
通过本次实验,学习了图的邻接矩阵和邻接表表示,图的深度优先和广度优先搜索方法。