• 第九篇博客


    这个作业属于哪个班级 数据结构--网络2011/2012
    这个作业的地址 DS博客作业04--图
    这个作业的目标 学习图结构设计及相关算法
    姓名 卢伟杰

    0.PTA得分截图

    图题目集总得分,请截图,截图中必须有自己名字。题目至少完成2/3,否则本次作业最高分5分。

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

    本次所有总结内容,请务必自己造一个图(不在教材或PPT出现的图),围绕这个图展开分析。建议:Python画图展示。图的结构尽量复杂,以便后续可以做最短路径、最小生成树的分析。

    1.1 图的存储结构

    1.1.1 邻接矩阵

    • 造一个图,展示其对应邻接矩阵

    • 邻接矩阵的结构体定义

    typedef struct 
    {
            char vexs[MAXN];
    	int arc[MAXN][MAXN];
    	int numVertexes,numEdges;
    }MGraph; 
    
    • 建图函数
    void GreateMGraph(MGraph *G)
    {
          int i,j,k,w;
          cout<<"请输入顶点数和边数:"<<endl;
          scanf("%d%d",&G->numVertexes,&G->numEdges);
          for(i=0;i<G->numVertexes;i++)
              scanf(&G->vexs[i]); 
    
          for(i=0;i<G->numVertexes;i++)
    	  for(j=0;j<G->numVertexes;j++)
    		G->arc[i][j]=INFINITY;
    
          for(int k=0;k<G->numVertexes;k++)
          {
    	  cout<<"输入边(vi,vj)上的下标i,下标j和权w:"<<endl;
    	  cin>>i>>j>>w;
    	  G->arc[i][j]=w;
    	  G->arc[j][i]=G->arc[i][j];
          } 
    
     } 
    
    

    1.1.2 邻接表

    • 造一个图,展示其对应邻接表

    • 邻接矩阵的结构体定义

    typedef  struct EdgeNode 
    {
          int adjvex; 
          EdgeType weight;
          struct EdgeNode *next; 
    } EdgeNode;
    
    typedef  struct VextexNode 
    {
          VertexType data; 
          EdgeNode *firstedge; 
    } VextexNode, AdjList[MAXVEX];
    
    typedef  struct
    {
          AdjList adjList;
          int numNodes, numEdges;  
    } GraphAdjList; 
    
    
    • 建图函数
    void CreateALGraph(GraphAdjList *Gp)
    {
         int i, j, k;
        EdgeNode *pe;
        cout <<  "输入顶点数和边数(空格分隔):" << endl;
        cin >> Gp->numNodes >> Gp->numEdges;
    
         for (i =  0 ; i < Gp->numNodes; i++)
        {
            cout <<  "输入顶点信息:" << endl;
            cin >> Gp->adjList[i].data;
            Gp->adjList[i].firstedge =  NULL; 
        }
    
         for (k =  0; k <  Gp->numEdges; k++) 
        {
            cout <<  "输入边(vi,vj)的顶点序号i,j(空格分隔):" << endl;
            cin >> i >> j;
            pe = (EdgeNode *)malloc( sizeof(EdgeNode));
            pe->adjvex = j; 
            pe->next = Gp->adjList[i].firstedge;
            Gp->adjList[i].firstedge = pe; 
            pe = (EdgeNode *)malloc( sizeof(EdgeNode));
            pe->adjvex = i;
            pe->next = Gp->adjList[j].firstedge;
            Gp->adjList[j].firstedge = pe;
    
        }
    } 
    
    

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

    各个结构适用什么图?时间复杂度的区别。

    • 采用邻接矩阵表示时,设邻接矩阵有n×n阶,矩阵包含n^2个元素。对每个顶点来说,
      搜索其所有邻接点需要搜索矩阵中对应的整个一行,因此,对整个图的遍历来说,
      需要搜索整个矩阵,算法的时间复杂度为O(n^2)

    • 采用邻接表表示时,若邻接表有n个结点和e条边,对每个顶点来说,搜索其所有邻接
      点需要搜索邻接表中对应的链表的各结点,算法的时间复杂度为O(n+e)

    1.2 图遍历

    1.2.1 深度优先遍历

    • 选上述的图,继续介绍深度优先遍历结果

    • v0, v1, v2, v3

    • 深度遍历代码

    void DFS(MGraph g, int v)
    {
        int i;
    
        visited[v-1] = 1;
        if (!flag)
        {
            cout << v;
            flag = 1;
        }
        else
        {
            cout << " " << v;
        }
    
        for (i = 0; i < g.n; i++)
        {
            if (g.edges[v-1][i] == 1 && visited[i] == 0)
            {
                DFS(g, i + 1);
            }
        }
    }
    
    
    • 深度遍历适用哪些问题的求解。(可百度搜索)

    • 用DFS(深度优先遍历)解决可达性/连通性问题

    private int m, n;
    private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }
        m = grid.length;
        n = grid[0].length;
        int islandsNum = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] != '0') {
                    dfs(grid, i, j);
                    islandsNum++;
                }
            }
        }
        return islandsNum;
    }
    
    private void dfs(char[][] grid, int i, int j) {
        if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == '0') {
            return;
        }
        grid[i][j] = '0';
        for (int[] d : direction) {
            dfs(grid, i + d[0], j + d[1]);
        }
    }
    
    

    1.2.2 广度优先遍历

    • 选上述的图,继续介绍广度优先遍历结果

    • v0, v1, v3, v2

    • 广度遍历代码

    void BFS(MGraph g, int v)
    {
        int i, k;
        int cur_node;
        int queue[MAXV];
        int front, rear;
        front = rear = 0;
        visited[0] = 0;
    
        visited[v - 1] = 1;
        queue[rear++] = v;//enqueue
    
        cout << v;
    
        while (front != rear)
        {
            cur_node = queue[front++];
            for (i = 0; i < g.n; i++)
            {
                if (visited[i] == 0 && g.edges[cur_node-1][i] == 1)
                {
                    cout << " " << i + 1;
                    queue[rear++] = i + 1;
                    visited[i] = 1;
                }
            }
        }
    }
    
    
    • 广度遍历适用哪些问题的求解。(可百度搜索)

    • 通常用于求解无向图的最短路径问题

    1.3 最小生成树

    用自己语言描述什么是最小生成树。

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

    1.3.1 Prim算法求最小生成树

    • 实现Prim算法的2个辅助数组是什么?其作用是什么?Prim算法代码。

    • 两个数组数组closest和lowcost,分别记录V-U顶点j到U中顶点的最小边。

    void Prim(Graph G)
     {
    	for(int i=0;i<G.vertexNum;i++)
            {
    		lowcost[i]=G.arc[0][i];adjvex[i]=0;	
    	}
    	lowcost[0]=0;	
    	for(int i=1;i<G.vertexNum;i++)
            {		
    		k=MinEdge(lowcost,G.vertextNum);	
    		cout<<k<<adjvex[k]<<lowcost[k];		
    		lowcost[k]=0;		
    		for(int j=1;j<G.vertexNum;j++)
                    {		
    			if(G.arc[k][j]<lowcost[j])
                            {		
    				lowcost[j]=G.arc[k][j];
    				adjvex[j]=k;		
    			}
    		}
    	}
    }
    
    
    • 分析Prim算法时间复杂度,适用什么图结构,为什么?

    • Prim算法的时间复杂度为O(n^2),适用于稠密图的最小生成树,两重for循环

    1.3.2 Kruskal算法求解最小生成树

    • 实现Kruskal算法的辅助数据结构是什么?其作用是什么?Kruskal算法代码。

    • vset[i]用来记录一个顶点i所在的连通分量编号

    void Kruskal(MGraph *G) 
    {
    	Edge EdgeCount[10]; 
    	int vset[MAX_V];
    	int k = 0;
    	int m = G->v;
    	int i,j,s1,s2;
    	for(i = 0; i < G->v; i++)
    	{
    	  for(j = 0; j < G->v; j++)
    	  {
    	  	  if(G->matrix[i][j] != 0)
    	  	  {
    	  	  	  if(i < j)
    			  {
    			      EdgeCount[k].v1 = i;
    				  EdgeCount[k].v2 = j;
    				  EdgeCount[k].weight = G->weight[i][j];
    				  k++; 
    			  }
    	  	  }
    	  }
    	}
            Sort(EdgeCount, G);
            for (i=0;i<G->v;i++)  
            {  
                vset[i]=i;  
            }  
            i = 0;
    	while(m > 1)
    	{
    		s1 = vset[EdgeCount[i].v1];
    		s2 = vset[EdgeCount[i].v2];
    		if(s1 != s2)
    		{
    			cout << EdgeCount[i].v1 << "--" << EdgeCount[i].v2 << endl;
    			m--;
    			for(j = 0; j < G->v; j++)
    			{
    				if(vset[j] == s2)
    				  vset[j] = s1;
    			}
    		}
    		i++;		
    	}	
    }
    
    • 分析Kruskal算法时间复杂度,适用什么图结构,为什么?

    • Kruskal算法的时间复杂度为O(e*loge) e为边数。克鲁斯卡尔算法主要针对边展开,
      边数少时效率会很高,所以对于稀疏图有优势。这个复杂度就是快排需要的时间

    1.4 最短路径

    1.4.1 Dijkstra算法求解最短路径

    • 基于上述图结构,求解某个顶点到其他顶点最短路径。(结合dist数组、path数组求解)

    • Dijkstra算法需要哪些辅助数据结构

    • Dijkstra算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径,它的主要
      特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止

    • 最小索引堆为辅助结构

    • Dijkstra算法如何解决贪心算法无法求最优解问题?展示算法中解决的代码

    void Dijkstra(MatGraph g;int v)
    {
          int dist[MAXV],path[MAXV];
          int S[MAXV];
          int MINdis,i,j,u;
          for(I=0;i<g.n;i++)
          {
                dist[i]=g.edges[v][I];
                S[I]=0;
                if(g.edges[v][I]<INF)
                path[I]=v;
                else
                path[I]=-1;
          }
          S[v]=1;path[v]=0;
          for(I=0;i<g.n-1;i++)
          {
                MINdis=INF;
                for(j=0;j<g.n;j++)
                if(S[j]==0&&dist[j]<MINdis)
                {
                      u=j;
                      MINdis=dist[j];
                }      
                S[u]=1;
                for(j=0;j<g.n;j++)
                if(g.edges[u][j]<INF&&dist[u]+g.edges[u][j]<dist[j])
                {
                      dist[j]=dist[u]+g.edges[u][j];
                      path[j]=u;
                }
           }
    }
    
    • Dijkstra算法的时间复杂度,使用什么图结构,为什么。

    • Dijkstra算法时间复杂度为O(n^2),邻接矩阵

    1.4.2 Floyd算法求解最短路径

    • Floyd算法解决什么问题?

    • 适用于APSP(All Pairs Shortest Paths,多源最短路径),是一种动态规划算法,稠密图效果最佳,边权可正可负

    • Floyd算法需要哪些辅助数据结构

    • 二维数组用于存放当前顶点之间的最短路径长度

    • Floyd算法优势,举例说明。

    • 此算法简单有效,由于三重循环结构紧凑,对于稠密图,效率要高于执行|V|次Dijkstra算法

    最短路径算法还有其他算法,可以自行百度搜索,并和教材算法比较。

    1.5 拓扑排序

    • 找一个有向图,并求其对要的拓扑排序序列

    • 3, 1, 4,2, 6, 5

    • 实现拓扑排序代码,结构体如何设计?

    • 拓扑排序结构体

    typedef struct
    {
          Vertex data;
          int count;
          ArcNode *firstarc;
    }VNode;
    
    • 书写拓扑排序伪代码,介绍拓扑排序如何删除入度为0的结点?
      void TopSort(Graph g)
        {
            for (int i=0; i<vertexnum; i++)
            {
                vertex v = FindZeroIndegree(g);
                if (v is not vertex)     
                {   
                    cout <<"the graph has cycle"<<endl;
                }
                cout << v ;
                foreach vertex w adjacent to v
                    w.indegree--;
            }
        } 
    
    • 如何用拓扑排序代码检查一个有向图是否有环路?
     while (top>-1)			
             {	  
                i=St[top];top--;			
    	    printf("%d ",i);		
    	    p=G->adjlist[i].firstarc;		
    	    while (p!=NULL)	
    	    {      
                     j=p->adjvex;
    	         G->adjlist[j].count--;
    	         if (G->adjlist[j].count==0)	
    	         {      
                       top++;
    		   St[top]=j;
    	         }
    	         p=p->nextarc;		
    	    }
            }
    

    1.6 关键路径

    • 什么叫AOE-网?

    • 在带权有向图中若以顶点表示事件,有向边表示活动,边上的权值表示该活动持续的时间

    • 什么是关键路径概念?

    • 关键路径是指设计中从输入到输出经过的延时最长的逻辑路径。优化关键路径是一种提高
      设计工作速度的有效方法。一般地,从输入到输出的延时取决于信号所经过的延时最大路径,
      而与其他延时小的路径无关。在优化设计过程中关键路径法可以反复使用,直到不可能减少
      关键路径延时为止。EDA工具中综合器及设计分析器通常都提供关键路径的信息以便设计者
      改进设计,提高速度。

    • 什么是关键活动?

    • 关键活动是为准时完成项目而必须按时完成的活动。即处于关键路径上的活动。所有项目都是
      由一系列活动组成,而在这些活动中存在各种链接关系和活动约束。其中有些活动如果延误就
      会影响整个项目工期。在项目中总存在这样一类直接影响项目工期变化的活动

    2.PTA实验作业(4分)

    2.1 六度空间(2分)

    选一题,介绍伪代码,不要贴代码。请结合图形展开分析思路。

    2.1.1 伪代码(贴代码,本题0分)

    while(队列不空)
        temp=队首的元素
        队首元素出队并且将其改为已访问
        while(p不为空)
            if(p->adjvex未被访问)
                该结点进队并标记为已访问
                cnt加一;
                tail记录此时结点的值
        end while
        if(队首等于该层最后一个结点)
            层数加一,last重置为队尾元素
        if(层数为6)
            结束
      返回标记的结点个数
    

    伪代码为思路总结,不是简单翻译代码。

    2.1.2 本题知识点

    • 邻接矩阵、辅助结构为二维数组,再用BFS遍历

    2.2 村村通或通信网络设计或旅游规划(2分)

    2.2.1 伪代码(贴代码,本题0分)

    Prim函数
    {
          int cnt=1,cost=0, v;
          for i=1 to N
              parent[i]等于s;
              dist[i] 等于c[i];
              parent[s]等于-1;
          dist[s]等于0;
          while
          v=findmin();
          if(v==-1)结束循环;
          cost+=dist[v];
          cnt++;
          dist[v]=0;
          for i=1 to N
                if dist[i]不等于0且c[v][i]小于dist[i]
                      dist[i]等于c[v][i];
                      parent[i]等于v;
          if(cnt等于N)输出cost;
          else 输出-1;
    
    }
    

    伪代码为思路总结,不是简单翻译代码。

  • 相关阅读:
    activity学习参考
    Activity工作流入门
    Mysql 命令查看函数,触发器。。
    本地redis下载并启动
    Could not initialize class com.taobao.diamond.client.impl.DiamondEnvRepo
    mysql把表(表已有数据)的某列属性由空到非空,以及常用列操作
    easyexcel导出两种方式response返回文件流下载和保存到服务器返回下载链接
    EasyExcel导出带下拉框,并解决导出之后打开总是显示发现不可读取内容
    el-table的fixed固定列属性导致数据错位
    el-input按回车时,解决同时触发回车和失焦事件的问题
  • 原文地址:https://www.cnblogs.com/eau2077/p/14802452.html
Copyright © 2020-2023  润新知