第九篇博客

| 这个作业属于哪个班级 |


| ---- | ---- | ---- |
| 这个作业的地址 |
| 这个作业的目标 | 学习图结构设计及相关算法 |
| 姓名 | 卢伟杰 |

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;

}

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

posted @ 2021-05-23 22:35  昨日云流  阅读(53)  评论(0编辑  收藏  举报