• DS博客作业04--图


    0.PTA得分截图

    1.本周学习总结(6分)

    1.1 图的存储结构

    1.1.1 邻接矩阵

    邻接矩阵

    邻接矩阵的存储方式是用两个数组表示图。一个一维数组存储图中顶点信息。一个二维数组(邻接矩阵)存储图中的边或弧的信息。

    邻接矩阵的结构体定义

    typedef struct
    {
    	VertexType vexs[MAXVEX];//顶点表
    	EdgeType arc[MAXVEX][MAXVEX];//邻接矩阵,可看作边表
    	int numVertexes,numEdges;//图中当前的顶点数和边数
    }MGraph;
    

    建图函数

    int creat_graph(MGraph *g){
      int i,j,k,w;
      //输入顶点数和边数
      cin>>g->numVertexes>>g->numEdges;
      //初始化邻接矩阵
      for(i=0;i<g->numVertexes;i++){
        for( j=0;j<g->numVertexes;j++){
          g->arc[i][j]=INFINITY;
        }
      }
      //用边数在邻接表中进行赋值操作
      for( k=0;k<g->numEdges;k++){
        cin>>i>>j>>w;
        g->arc[i][j]=w;
        //有向图时不需要这个,无向图时需要加上
        g->arc[j][i]=g->arc[i][j];
      }
      return ok;
    }
    

    1.1.2 邻接表

    邻接表,存储方法跟树的孩子链表示法相类似,是一种顺序分配和链式分配相结合的存储结构。如这个表头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中。
    对于无向图来说,使用邻接表进行存储也会出现数据冗余,表头结点A所指链表中存在一个指向C的表结点的同时,表头结点C所指链表也会存在一个指向A的表结点。

    邻接表的结构体定义

    typedef struct EdgeNode{
        int adjvex;//存储他弧的信息
        struct EdgeNode *next;//指向下一个
    }EdgeNode;
    //顶点表结点
    typedef struct VertexNode{
        VertexType data;//数据域
        EdgeNode *firstedge;//指针域
    }VertexNode,AdjList[MAXVEX];
    typedef struct{
        AdjList adjlist;
        int numVertexes,numEdges;//点的数目和边的数目
    }GraphAdjList;
    

    建图函数

    
    //——————————创建邻接表——————————
    int creat(GraphAdjList *g){
      int i,j,k;//for循环用到
      EdgeNode *q;
      //输入顶点和边
      cin>>g->numVertexes>>g->numEdges;
      //输入顶点信息
      for(i = 0; i < g->numVertexes; i++){
        cin>>g->adjlist[i].data;
        g->adjlist[i].firstedge=NULL;
      }
      //建立边表信息
      for(k = 0; k < g->numEdges; k++){
        cin>>i>>j;//表示他的坐标
        q=(EdgeNode *)malloc(sizeof(EdgeNode));
        q->adjvex=j;//存储弧头
        q->next=g->adjlist[i].firstedge;
        g->adjlist[i].firstedge=q;
        //当该图是有向图时,下面的操作继续执行
        q=(EdgeNode *)malloc(sizeof(EdgeNode));
        q->adjvex=i;
        q->next=g->adjlist[j].firstedge;//头插法产生逆序结构
        g->adjlist[j].firstedge=q;//firstedge一直在前面
      }
      return ok;
    }
    

    1.1.3 邻接矩阵和邻接表表示图的区别

    (1)联系:邻接表中每个链头后的所有边表结点对应邻接矩阵中的每一行,邻接表中的每个边表结点对应邻接矩阵该行的一个非零元素。
    (2)区别:
    对于任一确定的无向图,邻接矩阵是唯一的(行列号与顶点编号一致),但邻接表不唯一(链接次序与顶点编号无关)。
    邻接矩阵的空间复杂度为0(n2),而邻接表的空间复杂度为0(n+e)。
    如果图中边的数目远远小于n2称作稀疏图,这是用邻接表表示比用邻接矩阵表示节省空间;
    如果图中边的数目接近于n2,对于无向图接近于n*(n-1)称作稠密图,考虑到邻接表中要附加链域,采用邻接矩阵表示法为宜。

    1.2 图遍历

    1.2.1 深度优先遍历

    事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.

    对上无向图进行深度优先遍历,从A开始:
    访问顺序是:A -> B -> G -> E -> C -> D -> H -> F

    深度遍历代码

    int visited[MAX]= {0};//全局数组
    void DFS(AdjGraph * G, int v)//深度优先遍历算法
    {
    ArcNode * p;
    visited[v]=1;//置已访问标记
    printf("%d”,v);//输出被访问顶点的编号
    p=G-> adjlist[v]. firstarc;//p指向顶点v的第一个邻接点
    while (p!= NULL)
    { if (visited[p-> adjvex]==0) //若p -> adjvex顶点未被访问,递归访问它
    DFS(G,p -> adjvex);
    p=p - > nextarc;//p指向顶点v的下一个邻接点
    }
    }
    

    深度遍历适用哪些问题的求解。

    1:全排列问题
    2:ABC+DEF=GHI问题
    3:二维数组寻找点到点的最短路径
    4:求岛屿的面积
    5:求岛屿的个数
    6:地板的埔法有多少种
    7:二叉树的深度遍历
    8:图的深度遍历
    9:图的最短路径求解
    10:找子集等于某给定的数

    1.2.2 广度优先遍历

    宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。

    访问顺序是:A -> B -> C -> D -> F -> G -> E -> H

    广度遍历代码

    void graph_bfs(Graph *pg){
    	printf("广度遍历:");
    	Queue que;
    	queue_init(&que,pg->vertexnum);
    	bool visited[pg->vertexnum];
    	int i=0;
    	for(i=0;i<pg->vertexnum;i++){
    		visited[i] = false;	//顶点没有遍历
    	}
    	for(i=0;i<pg->vertexnum;i++){
    		if(!visited[i]){//顶点没有遍历
    			visited[i] = true;//设置为已经遍历
    			queue_push(&que,i);
    			while(!queue_is_empty(&que)){
    				int v = queue_pop(&que);	
    				printf("%c ",pg->vertex[v].key);
    				ENode *node = pg->vertex[v].enodes;
    				while(node != NULL){
    					int pos = get_position(pg,node->key);
    					if(!visited[pos]){
    						visited[pos] = true;
    						queue_push(&que,pos);
    					}
    					node = node->next;
    				}
    			}
    		}
    	}
    	printf("
    ");
    	queue_destroy(&que);
    }
    

    广度遍历适用哪些问题的求解。

    BFS算法是和解决最短路径的问题,有些问题(比如下面)是可以抽象成图论中的图的两种优先遍历问题,能抽象出结点(图中的node),抽象出约束条件(图中的edge),这样的话,我们就能够用图中的相关理论来解决此问题。而对于BFS算法其实是一种层次遍历的算法,类似于二叉树中的层序遍历(相当于有个中心,向周围扩散的感觉),一般可以用于解决一些最短路径、最优路径问题(迷宫问题较为多见);而对于DFS算法相当于是回溯算法的思想,和二叉树中的先序遍历的思想一致,一个一个尝试直到找到需要的,一般可以解决路径查找有无问题、常见的回溯问题(排列、组合、二维数组遍历相关的问题)。

    1.3 最小生成树

    最小生成树就是一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的边最少。

    1.3.1 Prim算法求最小生成树

    2个辅助数组:
    1.closest[i]:最小生成树的边依附在U中顶点编号。
    2.lowcost[i]表示顶点i(i ∈ V-U)到U中顶点的边权重,取最小权重的顶点k加入U。
    并规定lowcost[k]=0表示这个顶点在U中
    每次选出顶点k后,要队lowcost[]和closest[]数组进行修正

    Prim算法代码。

    #define INF 32767//INF表示∞
    void  Prim(Graph G,int v)
    {     
          int lowcost[MAXV];
          int min;
          int closest[MAXV], i, j, k;
          for (i=0;i<G.n;i++)//给lowcost[]和closest[]置初值
          {	
            lowcost[i]=G.edges[v][i];
    	closest[i]=v;
          }
    
           for (i=1;i<G.n;i++)//输出(n-1)条边
           {	
              min=INF;
    	  for (j=0;j<G.n;j++) //在(V-U)中找出离U最近的顶点k
    	     if (lowcost[j]!=0 && lowcost[j]<min)
    	     {	
                    min=lowcost[j];
    		k=j;//k记录最近顶点编号
    	     }
    	     lowcost[k]=0;//标记k已经加入U
                 for (j=0;j<G.n;j++)//修改数组lowcost和closest
    	     if (lowcost[j]!=0 && G.edges[k][j]<lowcost[j])
    	     {	
                      lowcost[j]=G.edges[k][j];
    		  closest[j]=k;
    	     }
            }
    }
    

    分析Prim算法时间复杂度,适用什么图结构.

    Prim算法中有两重for循环,时间复杂度为O(n^2)。其执行时间与图中边数e无关,所以适合用稠密图求最小生成树。

    1.3.2 Kruskal算法求解最小生成树

    边的角度求网的最小生成树,时间复杂度为O(eloge)。和普里姆算法恰恰相反,更适合于求边稀疏的网的最小生成树。
    对于任意一个连通网的最小生成树来说,在要求总的权值最小的情况下,最直接的想法就是将连通网中的所有边按照权值大小进行升序排序,从小到大依次选择。
    (1)置U的初值等于V(即包含有G中的全部顶点),TE(表示最小生成树的边集)的初值为空集(即图T中每一个顶点都构成一个连通分量)。
    (2)将图G中的边按权值从小到大的顺序依次选取:

    Kruskal算法代码。

    #include "stdio.h"
    #include "stdlib.h"
    #define MAX_VERtEX_NUM 20
    #define VertexType int
    typedef struct edge{
        VertexType initial;
        VertexType end;
        VertexType weight;
    }edge[MAX_VERtEX_NUM];
    //定义辅助数组
    typedef struct {
        VertexType value;//顶点数据
        int sign;//每个顶点所属的集合
    }assist[MAX_VERtEX_NUM];
    
    assist assists;
    
    //qsort排序函数中使用,使edges结构体中的边按照权值大小升序排序
    int cmp(const void *a,const void*b){
        return  ((struct edge*)a)->weight-((struct edge*)b)->weight;
    }
    //初始化连通网
    void CreateUDN(edge *edges,int *vexnum,int *arcnum){
        printf("输入连通网的边数:
    ");
        scanf("%d %d",&(*vexnum),&(*arcnum));
        printf("输入连通网的顶点:
    ");
        for (int i=0; i<(*vexnum); i++) {
            scanf("%d",&(assists[i].value));
            assists[i].sign=i;
        }
        printf("输入各边的起始点和终点及权重:
    ");
        for (int i=0 ; i<(*arcnum); i++) {
            scanf("%d,%d,%d",&(*edges)[i].initial,&(*edges)[i].end,&(*edges)[i].weight);
        }
    }
    //在assists数组中找到顶点point对应的位置下标
    int Locatevex(int vexnum,int point){
        for (int i=0; i<vexnum; i++) {
            if (assists[i].value==point) {
                return i;
            }
        }
        return -1;
    }
    int main(){
       
        int arcnum,vexnum;
        edge edges;
        CreateUDN(&edges,&vexnum,&arcnum);
        //对连通网中的所有边进行升序排序,结果仍保存在edges数组中
        qsort(edges, arcnum, sizeof(edges[0]), cmp);
        //创建一个空的结构体数组,用于存放最小生成树
        edge minTree;
        //设置一个用于记录最小生成树中边的数量的常量
        int num=0;
        //遍历所有的边
        for (int i=0; i<arcnum; i++) {
            //找到边的起始顶点和结束顶点在数组assists中的位置
            int initial=Locatevex(vexnum, edges[i].initial);
            int end=Locatevex(vexnum, edges[i].end);
            //如果顶点位置存在且顶点的标记不同,说明不在一个集合中,不会产生回路
            if (initial!=-1&& end!=-1&&assists[initial].sign!=assists[end].sign) {
                //记录该边,作为最小生成树的组成部分
                minTree[num]=edges[i];
                //计数+1
                num++;
                //将新加入生成树的顶点标记全不更改为一样的
                for (int k=0; k<vexnum; k++) {
                    if (assists[k].sign==assists[end].sign) {
                        assists[k].sign=assists[initial].sign;
                    }
                }
                //如果选择的边的数量和顶点数相差1,证明最小生成树已经形成,退出循环
                if (num==vexnum-1) {
                    break;
                }
            }
        }
        //输出语句
        for (int i=0; i<vexnum-1; i++) {
            printf("%d,%d
    ",minTree[i].initial,minTree[i].end);
        }
        return 0;
    }
    

    分析Kruskal算法时间复杂度,适用什么图结构.

    边的角度求网的最小生成树,时间复杂度为O(eloge)。和普里姆算法恰恰相反,更适合于求边稀疏的网的最小生成树。

    1.4 最短路径

    1.4.1 Dijkstra算法求解最短路径

    1.从T中选取一个其距离值为最小的顶点W, 加入S
    2.S中加入顶点w后,对T中顶点的距离值进行修改:
    若加进W作中间顶点,从V0到Vj的距离值比不加W的路径要短,则修改此距离值;
    3.重复上述步骤1,直到S中包含所有顶点,即S=V为止。

    代码

    typedef struct 
    {    int u;     //边的起始顶点
         int v;      //边的终止顶点
         int w;     //边的权值
    } Edge; 
    Edge E[MAXV];
    void Kruskal(Graph G)
    {    
          int i,j,u1,v1,sn1,sn2,k;
          int vset[MAXV];
          Edge E[MaxSize];	//存放所有边
          k=0;			//E数组的下标从0开始计
          for (i=0;i<G.n;i++)	//由G产生的边集E
    	for (j=0;j<G.n;j++)
    	      if (G.edges[i][j]!=0 && G.edges[i][j]!=INF)
    	      {     
                         E[k].u=i;  E[k].v=j;  E[k].w=G.edges[i][j];
    	             k++;
    	      }
            InsertSort(E,G.e);	//用直接插入排序对E数组按权值递增排序
            for (i=0;i<g.n;i++) 	//初始化辅助数组
    	vset[i]=i;
    	k=1;		//k表示当前构造生成树的第几条边,初值为1
    	j=0;		//E中边的下标,初值为0
    	while (k<G.n)	//生成的边数小于n时循环
    	{ 
                  u1=E[j].u;v1=E[j].v;	//取一条边的头尾顶点
    	      sn1=vset[u1];
    	      sn2=vset[v1];		//分别得到两个顶点所属的集合编号
     	      if (sn1!=sn2)  	//两顶点属于不同的集合
    	      {
                    k++;		   	//生成边数增1
    		for (i=0;i<g.n;i++)  	//两个集合统一编号
    		    if (vset[i]==sn2) 	//集合编号为sn2的改为sn1
    		       vset[i]=sn1;
    	      }
    	     j++;			   //扫描下一条边
            }
    }
    

    Dijkstra算法的时间复杂度,使用什么图结构

    1.时间复杂度为O(n^2)。
    2.思路:广度优先 + 松弛
    所有点分为两个集合S SS和T TT,S SS最开始只包括源点s ss,剩余点都位于T TT。S SS集合表示已经计算出最短路径的点集合,T TT表示尚未计算出最短路径的点集合。
    每次从集合T TT中选出一个与集合S SS距离最短的点v vv,将点v vv加入集合S SS。通过点v vv对集合T TT中的点进行松弛,即更新T TT中点的最短距离。
    不断重复此步骤2,直至T集合中无法找出与集合S SS相邻的点。
    关键点:每次从T TT中选出的点,距离源点的距离一定不会被松弛,因此每次选出的点都将加入集合S SS.。

    1.4.2 Floyd算法求解最短路径

    Floyd算法适用于APSP(All Pairs Shortest Paths,多源最短路径),是一种动态规划算法,稠密图效果最佳,边权可正可负。此算法简单有效,由于三重循环结构紧凑,对于稠密图,效率要高于执行|V|次Dijkstra算法,也要高于执行V次SPFA算法。

    代码

    void Floyd(Graph G)		
    {     
        int A[MAXVEX][MAXVEX];	//建立A数组
        int path[MAXVEX][MAXVEX];	//建立path数组
        int i, j, k;
       for (i=0;i<G.n;i++)   		
           for (j=0;j<G.n;j++) 
           {       
              A[i][j]=G.edges[i][j];
    	 if (i!=j && G.edges[i][j]<INF)
    	      path[i][j]=i; 	//i和j顶点之间有一条边时
           else			 
    	      path[i][j]=-1;
           }
    
      for (k=0;k<G.n;k++)		//求Ak[i][j]
     {     
         for (i=0;i<G.n;i++)
           for (j=0;j<G.n;j++)
    	    if (A[i][j]>A[i][k]+A[k][j])	//找到更短路径
    	    {   
                    A[i][j]=A[i][k]+A[k][j];	//修改路径长度
    	       path[i][j]=path[k][j]; 	//修改最短路径为经过顶点k
              }
      }
    }	
    

    优缺点

    优点:容易理解,可以算出任意两个节点之间的最短距离,代码编写简单。
    缺点:时间复杂度比较高,不适合计算大量数据。

    最短路径算法还有其他算法

    SPFA(Shortest Path Faster Algorithm)算法是求单源最短路径的一种算法,它是Bellman-ford队列优化,它是一种十分高效的最短路算法。
    实现方法:建立一个队列,初始时队列里只有起始点s,在建立一个数组记录起始点s到所有点的最短路径(初始值都要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里的点去刷新起始点s到所有点的距离的距离,如果刷新成功且刷新的点不在队列中,则把该点加入到队列,重复执行直到队列为空。
    此外,SPFA算法可以判断图中是否有负权欢=环,即一个点的入队次数超过N。

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,len;
    struct egde{
         int to,val,next;
    }e[200100];
    int head[200100],vis[200100],dis[200100];
    void add(int from,int to,int val){
         e[len].to=to;
         e[len].val=val;
         e[len].next=head[from];
         head[from]=len;
         len++;
    }
    void spfa()
    {
        queue<int>q;
        q.push(1);
        vis[1]=1;
        while(!q.empty())
        {
            int t=q.front();
            q.pop();
            vis[t]=0;
            for(int i=head[t];i!=-1;i=e[i].next){
                int s=e[i].to;
                if(dis[s]>dis[t]+e[i].val){
                    dis[s]=dis[t]+e[i].val;
                    if(vis[s]==0){
                        q.push(s);
                        vis[s]=1;
                    }
                }
            }
        }
     
    }
    int main(){
        int from,to,val;
        while(cin>>n>>m){
            memset(head,-1,sizeof(head));
            memset(vis,0,sizeof(vis));
           /* for(int i=1;i<=n;i++){
                dis[i]=99999999;
            }*/
            memset(dis,0x3f,sizeof(dis));
            dis[1]=0;len=1;
            for(int i=0;i<m;i++){
                cin>>from>>to>>val;
                add(from,to,val);
            }
            spfa();
            for(int i=1;i<=n;i++){
                cout<<dis[i]<<" ";
            }
            cout<<endl;
        }
    }
    

    1.5 拓扑排序

    实现拓扑排序代码

    #include <stdio.h>
    #include <stdlib.h>
    #define  MAX_VERTEX_NUM 20//最大顶点个数
    #define  VertexType int//顶点数据的类型
    typedef enum{false,true} bool;
    typedef struct ArcNode{
        int adjvex;//邻接点在数组中的位置下标
        struct ArcNode * nextarc;//指向下一个邻接点的指针
    }ArcNode;
    typedef struct VNode{
        VertexType data;//顶点的数据域
        ArcNode * firstarc;//指向邻接点的指针
    }VNode,AdjList[MAX_VERTEX_NUM];//存储各链表头结点的数组
    typedef struct {
        AdjList vertices;//图中顶点及各邻接点数组
        int vexnum,arcnum;//记录图中顶点数和边或弧数
    }ALGraph;
    //找到顶点对应在邻接表数组中的位置下标
    int LocateVex(ALGraph G,VertexType u){
        for (int i=0; i<G.vexnum; i++) {
            if (G.vertices[i].data==u) {
                return i;
            }
        }
        return -1;
    }
    //创建AOV网,构建邻接表
    void CreateAOV(ALGraph **G){
        *G=(ALGraph*)malloc(sizeof(ALGraph));
       
        scanf("%d,%d",&((*G)->vexnum),&((*G)->arcnum));
        for (int i=0; i<(*G)->vexnum; i++) {
            scanf("%d",&((*G)->vertices[i].data));
            (*G)->vertices[i].firstarc=NULL;
        }
        VertexType initial,end;
        for (int i=0; i<(*G)->arcnum; i++) {
            scanf("%d,%d",&initial,&end);
           
            ArcNode *p=(ArcNode*)malloc(sizeof(ArcNode));
            p->adjvex=LocateVex(*(*G), end);
            p->nextarc=NULL;
           
            int locate=LocateVex(*(*G), initial);
            p->nextarc=(*G)->vertices[locate].firstarc;
            (*G)->vertices[locate].firstarc=p;
        }
    }
    //结构体定义栈结构
    typedef struct stack{
        VertexType data;
        struct stack * next;
    }stack;
    //初始化栈结构
    void initStack(stack* *S){
        (*S)=(stack*)malloc(sizeof(stack));
        (*S)->next=NULL;
    }
    //判断链表是否为空
    bool StackEmpty(stack S){
        if (S.next==NULL) {
            return true;
        }
        return false;
    }
    //进栈,以头插法将新结点插入到链表中
    void push(stack *S,VertexType u){
        stack *p=(stack*)malloc(sizeof(stack));
        p->data=u;
        p->next=NULL;
        p->next=S->next;
        S->next=p;
    }
    //弹栈函数,删除链表首元结点的同时,释放该空间,并将该结点中的数据域通过地址传值给变量i;
    void pop(stack *S,VertexType *i){
        stack *p=S->next;
        *i=p->data;
        S->next=S->next->next;
        free(p);
    }
    //统计各顶点的入度
    void FindInDegree(ALGraph G,int indegree[]){
        //初始化数组,默认初始值全部为0
        for (int i=0; i<G.vexnum; i++) {
            indegree[i]=0;
        }
        //遍历邻接表,根据各链表中结点的数据域存储的各顶点位置下标,在indegree数组相应位置+1
        for (int i=0; i<G.vexnum; i++) {
            ArcNode *p=G.vertices[i].firstarc;
            while (p) {
                indegree[p->adjvex]++;
                p=p->nextarc;
            }
        }
    }
    void TopologicalSort(ALGraph G){
        int indegree[G.vexnum];//创建记录各顶点入度的数组
        FindInDegree(G,indegree);//统计各顶点的入度
        //建立栈结构,程序中使用的是链表
        stack *S;
        initStack(&S);
        //查找度为0的顶点,作为起始点
        for (int i=0; i<G.vexnum; i++) {
            if (!indegree[i]) {
                push(S, i);
            }
        }
        int count=0;
        //当栈为空,说明排序完成
        while (!StackEmpty(*S)) {
            int index;
            //弹栈,并记录栈中保存的顶点所在邻接表数组中的位置
            pop(S,&index);
            printf("%d",G.vertices[index].data);
            ++count;
            //依次查找跟该顶点相链接的顶点,如果初始入度为1,当删除前一个顶点后,该顶点入度为0
            for (ArcNode *p=G.vertices[index].firstarc; p; p=p->nextarc) {
                VertexType k=p->adjvex;
                if (!(--indegree[k])) {
                    //顶点入度为0,入栈
                    push(S, k);
                }
            }
        }
        //如果count值小于顶点数量,表明该有向图有环
        if (count<G.vexnum) {
            printf("该图有回路");
            return;
        }
    }
    int main(){
        ALGraph *G;
        CreateAOV(&G);//创建AOV网
        TopologicalSort(*G);//进行拓扑排序
        return  0;
    }
    

    书写拓扑排序伪代码

    遍历邻接表
       计算每个顶点的入度,存入头结点count成员中;
    遍历图顶点
       找到一个入度为0的顶点,入栈/队列/数组;
    while(栈不为空)
       出栈结点v,访问;
       遍历v的所有邻接点
       {
          所有邻接点的入度-1;
          若有邻接点入度为0,入栈/队列/数组;
       }
    

    如何用拓扑排序代码检查一个有向图是否有环路?

    (1)在有向图中选一个没有前驱的顶点且输出之
    (2)从图中删除该顶点和所有以它为尾的弧。
    重复上述两步,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止,后一种情况则说明有向图中存在环。

    1.6 关键路径

    什么叫AOE-网?

    在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,边上的权值表示活动的持续时间,称这样的有向图叫做边表示活动的网,简称AOE网。AOE网中没有入边的顶点称为始点(或源点),没有出边的顶点称为终点(或汇点)。
    AOE网的性质:
    ⑴ 只有在某顶点所代表的事件发生后,从该顶点出发的各活动才能开始;
    ⑵ 只有在进入某顶点的各活动都结束,该顶点所代表的事件才能发生。

    什么是关键路径概念?

    关键路径:在AOE网中,从始点到终点具有最大路径长度(该路径上的各个活动所持续的时间之和)的路径称为关键路径。

    什么是关键活动?

    关键活动:关键路径上的活动称为关键活动。关键活动:e[i]=l[i]的活动
      由于AOE网中的某些活动能够同时进行,故完成整个工程所必须花费的时间应该为始点到终点的最大路径长度。关键路径长度是整个工程所需的最短工期。

    2.PTA实验作业(4分)

    2.1 六度空间(2分)

    2.1.1 伪代码

    定义队列q,让结点v入队
    将结点v标记为已访问
    while(队列不空的时候)
        temp=队首元素
        队首元素出队并标记为已访问
        while(p不为空)
            if(p->adjvex未被访问)
                该结点进队并标记为已访问
                记录有联系结点个数的num加一
                tail记录此时结点的值
        end while
        if(队首等于该层最后一个结点)
            层数加一,last重置为队尾元素
        if(层数为6)
            此时可以不再进行结点记录,直接结束
    最后返回记录标记的结点个数num
    

    2.1.2 提交列表


    2.1.3 本题知识点

    1.使用BFS算法,遍历
    2.在学习BFS的过程中,巩固队列的知识
    3.将广度遍历BFS和递归相结合,分层运算

    2.2 村村通(2分)

    2.2.1 伪代码

      for (i = 1; i <= n; i++)
            {
                    给lowcost[]和closest[]置初值;
               }
            for (i = 1; i <n; i++)
            {
                    给min赋初值(表示无穷);
                   for (j = 1; j <= n; j++) 
                    {
                        在(V-U)中找出离U最近的顶点k
                         k记录最近顶点的编号
                        }
                   for (j = 1; j <= n; j++) 
                    {
                           对(V-U)中的顶点j 进行调整;
                            修改数组lowcost[]和closest[];
                    }
                }
            输出num;
    

    2.2.2 提交列表

    2.2.3 本题知识点

    1.使用Prim算法,计算出最短路径
    2.Prim算法需要两个辅助数组,closest[i]为最小生成树的边依附在U中顶点编号,lowcost[i]表示顶点i到U中顶点的边权重
    3.学习在结构体中使用二级指针,简化结构体

  • 相关阅读:
    jQuery标准的AJAX模板
    maven库
    在 Windows7 上按照 MySQL5.7
    如何保证代码的有效性
    项目负责人的职责
    string integer == equals 转
    走近AbstractQueuedSynchronizer
    STAR
    tesseract-ocr
    Spring @Qualifier l转
  • 原文地址:https://www.cnblogs.com/f2002/p/14802304.html
Copyright © 2020-2023  润新知