0.PTA得分截图

1.本周学习总结

1.1 总结图内容

1.1 图存储结构

  • 邻接矩阵
  1. 结构体
#define  MAXV  20
typedef struct {  	        //图的定义
    int edges[MAXV][MAXV]; 	//邻接矩阵
    int n,e;  		//顶点数,弧数
} MGraph;                    //图的邻接矩阵表示类型
  1. 建立代码
void CreateMGraph(MGraph &g, int n, int e)
{
	int i, j, k;

	for (i = 0; i <= n; i++)//矩阵初始化
	{
		for (j = 0; j <= n; j++)
		{
			g.edges[i][j] = 0;
		}
	}

	for (k = 1; k <= e; k++)
	{
		cin >> i >> j;
		g.edges[i][j] = 1;
		g.edges[j][i] = 1;
	}
	g.e = e;
        g.n = n;1. 结构体
}
  1. 特点:
    • 适用于稠密图
    • 无向图一定轴对称,有向图不一定
    • 每点由行指向列
  • 邻接表
  1. 结构体
#define  MAXV  20
typedef struct ANode {
    int adjvex;		//该边的终点编号
    struct ANode *nextarc;	//指向下一条边的指针
    int info;	//该边的相关信息,如权重
} ArcNode;			//边表节点类型
typedef struct Vnode {
    int data;			//顶点信息
    ArcNode *firstarc;	//指向第一条边
} VNode;			//邻接表头节点类型
typedef struct { 
    VNode adjlist[MAXV];		//邻接表
    int n,e;		//图中顶点数n和边数e
} AdjGraph;	
  1. 建立代码
void CreateGraph(Adjgraph *&g, int V, int E)
{
	int i, j, a, b;
	ArcNode *p;
	g = new Adjgraph;

	for (i = 1; i <= V; i++)
		g->adjlist[i].firstarc = NULL;

	for (i = 1; i <= E; i++)
	{
		cin >> a >> b;
		p = new ArcNode;
		p->adjnode = b;
		//p->color = -1;
		p->nextarc = g->adjlist[a].firstarc;
		g->adjlist[a].firstarc = p;

		p = new ArcNode;
		p->adjnode = a;
		//p->color = -1;
		p->nextarc = g->adjlist[b].firstarc;
		g->adjlist[b].firstarc = p;
	}
	g->n = V;
	g->e = E;
}
  1. 特点:
    • 适用于稀疏图

1.2图遍历及应用。包括DFS,BFS.如何判断图是否连通、如何查找图路径、如何找最短路径。

  1. DFS
以邻接表为存储结构

void DFS(AdjGraph *G, int v)
{
	int i, j;
	ArcNode *p;
	visited[v] = 1;

	if (flag == 0)
	{
		cout << v;
		flag = 1;
	}
	else
		cout << " " << v;

	p = G->adjlist[v].firstarc;

	while (p)
	{
		if (!visited[p->adjvex])
			DFS(G, p->adjvex);
		p = p->nextarc;
	}	
}

以邻接矩阵为存储结构

void DFS(MGraph g, int v)
{
	int i,j;
	i = v;
	visited[v] = 1;
	
	if (flag == 0)
	{
		cout << v;
		flag = 1;
	}
	else
		cout << " " << v;

	for (j = 1; j <= g.n; j++)
	{
		if(g.edges[i][j]&&!visited[j])
			DFS(g, j);
	}
}

  1. BFS
邻接矩阵:
void BFS(MGraph g, int v)
{
    visited[v] = 1;      //当前节点已访问过,数组值置为1
    queue<int>qu;
    qu.push(v);      //节点入队列
    cout << v << " ";
    int item, i;
    while (!qu.empty())
    {
	      item = qu.front();
	      qu.pop();
	      for (i = 1; i <= g.n; i++)
	      {
		     if (!visited[i]&&g.edges[item][i]==1)      //节点i未访问过,且队头元素item表示的节点与节点i间有边
		     {
			     qu.push(i);      
			     cout << v << " ";
			     visited[i] = 1;
		     }
	      }
    }
}

邻接表:

void BFS(AdjGraph* G, int v)
{
    queue<int>qu;
    qu.push(v);
    ArcNode* ptr;
    int item;
    cout << v << " ";
    visited[v] = 1;      //当前节点已访问过,数组值置为1
    while (!qu.empty())
    {
	      item = qu.front();
	      ptr = G->adjlist[item].firstarc;      //边指针ptr指向item表示的节点所连的第一条边
	      while (ptr != NULL)
	      {
		      if (visited[ptr->adjvex] == 0)      //该边的终点还未被访问
		      {
			      qu.push(ptr->adjvex);
			      visited[ptr->adjvex] = 1;
			      cout << ptr->adjvex << " ";
		      }
		      ptr = ptr->nextarc;
	      }
	      qu.pop();
    }
}

1.3最小生成树相关算法及应用

1.3.1 定义:一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal算法或prim算法求出。

1.3.2 特点:边数一定为n-1;树不唯一

1.3.3 Prim算法

1. 特点:

  1. 时间复杂度:O(n2)
  2. 适用于稠密图,存储结构为邻接矩阵

2. 代码

void prim(MGraph g, int v)
{
  int lowcost[MAXV], min, i, j, k = 0;
  int closest[MAXV];
  int  sum = 0;
  for(i = 1; i <= g.n; i++)      //给数组lowcost[]和closest[]置初值
  {
  	lowcost[i] = g.edges[v][i];
  	closest[i] = v;
  }
  lowcost[v] = 0;      //顶点v已经加入树中
  for (i = 1; i < g.n; i++)      //找出(n-1)个顶点
  {
  	min = 10000;
  	k = 0;
  	for (j = 1; j <= g.n; j++)      //找出离树中节点最近的顶点k
  	{
  		if (lowcost[j] != 0 && lowcost[j] < min)
  		{
  			min = lowcost[j];
  			k = j;      //k记录最近顶点的编号
  		}
  	}
  	if (k == 0)      //不是连通图
  	{
  		cout << "-1" << endl;
  		return;
  	}
  	sum += min;      //变量sum存储最小生成树中边的权值
  	lowcost[k] = 0;      //顶点k已经加入树中
  	for (j = 1; j <= g.n; j++)
  	{
  		if (lowcost[j] != 0 && g.edges[k][j] < lowcost[j])
  		{
  			lowcost[j] = g.edges[k][j];
  			closest[j] = k;
  		}
  	}

  }
  cout << sum << endl;
}

1.3.4 Kruskal算法

1. 特点

  1. 时间复杂度:O(eloge);
  2. 适用于稀疏图,存储结构为邻接表

2. 代码:

typedef struct {
   int u;      //边的起始顶点
   int v;      //边的终止顶点
   int w;      //边的权值
}Edge;
//改进的克鲁斯卡尔算法(使用了堆排序,并查集)
void Kruskal(AdjGraph* g)
{
      int i,j,k,u1,v1,sn1,sn2;
      UFSTree t[MAXSize];     //并查集,树结构
      ArcNode* p; 
      Edge E[MAXSize];
      k=1;      //     E数组的下标从1开始计
      for(i = 0; i < g.n; i++)
      {
           p=g->adjlist[i].firstarc;
           while(p!=NULL)
           {
               E[k].u=i;
               E[k].v=p->adjvex;
               E[k].w=p->weight;
               k++;
               p=p->nextarc;
           }
      }
      HeapSort(E,g.e);      //采用堆排序对E数组按权值递增排序
      MAKE_SET(t,g.n);      //初始化并查集树t
      k=1;      //k表示当前构造生成树的第几条边,初值为1
      j=1;      //E中边的下标,初值为1
      while(k<g.n)      //生成的边数为n-1
      {
            u1=E[j].u;
            v1=E[j].v;      //取一条边的头尾顶点编号u1和v1
            sn1=FIND_SET(t,u1);
            sn2=FIND_SET(t,v1);      //分别得到两个顶点所属的集合编号
            if(sn1!=sn2)      //两顶点属不同集合
            {
                 k++;      //生成边数增1
                 UNION(t, u1, v1);      //将u1和v1两个顶点合并
            }
            j++;      //下一条边
      }
}

1.4 最短路径相关算法及应用,可适当拓展最短路径算法

  • Dijkstra算法
  1. 特点:

    1. 只能求一点到其他点的距离
  2. 代码:

void Dijkstra(MGraph 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;
	for (i = 0; i < g.n; 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 (s[j] == 0)
			{
				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;
				}
			}
		}
	}
}
  • Floyd算法
  1. 特点:

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

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
          }
  }
}	

1.5 拓扑排序、关键路径

  • 拓扑排序:在一个有向图中找到拓扑序列的过程
  • 代码:
typedef struct 	       	//表头结点类型
{     
      Vertex data;         	//顶点信息
      int count;           	//存放顶点入度
      ArcNode *firstarc;   	//指向第一条边
}VNode;

 void TopSort(AdjGraph *G)	//拓扑排序算法
{      
        int i,j;
        int St[MAXV],top=-1;	//栈St的指针为top
        ArcNode *p;
        for (i=0;i<G->n;i++)		//入度置初值0
	G->adjlist[i].count=0;
        for (i=0;i<G->n;i++)		//求所有顶点的入度
        {	
            p=G->adjlist[i].firstarc;
	    while (p!=NULL)
	   {        
                  G->adjlist[p->adjvex].count++;
	          p=p->nextarc;
	   }
        }
         
         for (i=0;i<G->n;i++)		//将入度为0的顶点进栈
	 if (G->adjlist[i].count==0)
	 {	
            top++;
	    St[top]=i;
	 }
         while (top>-1)			//栈不空循环
         {	  
            i=St[top];top--;			//出栈一个顶点i
	    printf("%d ",i);		//输出该顶点
	    p=G->adjlist[i].firstarc;		//找第一个邻接点
	    while (p!=NULL)		//将顶点i的出边邻接点的入度减1
	    {      
                 j=p->adjvex;
	         G->adjlist[j].count--;
	         if (G->adjlist[j].count==0)	//将入度为0的邻接点进栈
	         {      
                   top++;
		   St[top]=j;
	         }
	         p=p->nextarc;		//找下一个邻接点
	    }
        }
}

  • 关键路径:从有向图的源点到汇点的最长路径

1.2.谈谈你对图的认识及学习体会。

  • 图的认识: 图感觉是树的延伸,它的复杂度以及可应用的范围都变大了,从不带权值的朋友圈,到带权值的交通网络和计算机网络搭建,所以我想在日后的学习和工作中可以经常接触到这一块.
  • 学习体会:像上面说的图的应用较广,学习中也常接触生活中常见的问题,这给了我更多学习它的兴趣,但学习的过程注定是枯燥的,因此......PTA刷的不太行。

2.阅读代码(0--5分)

2.1 题目及解题代码

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* findMinHeightTrees(int n, int** edges, int edgesSize, int* edgesColSize, int* returnSize){
    int* pQueue = (int*)malloc(n * sizeof(int)); // 队列
    memset(pQueue, 0, n * sizeof(int));
    int front = -1; // 队头下标
    int rear = -1; // 队尾下标
    int curr = 0; // 当前队头元素
    int lenghOfQue = 0; // 队长

    if (1 == n && 0 == edgesSize)
    (
        *returnSize = 1;
        return pQueue;
    }

    int* pIndegree = (int*)malloc(n * sizeof(int)); // 入度数组
    memset(pIndegree, 0, n * sizeof(int)); // 入度初始为0

    int** ppGraph = (int**)malloc(n * sizeof(int*)); // 二维数组邻接表
    int* pColOfGraph = (int*)malloc(n * sizeof(int)); // 每个结点相邻结点个数
    memset(pColOfGraph, 0, n * sizeof(int));
    int row = 0;
    int col = 0;
    for (row = 0; row <= n - 1; row++)
    {
        ppGraph[row] = (int*)malloc(n * sizeof(int*));
    }

    for (row = 0; row <= edgesSize - 1; row++) // 初始化邻接表:图 入度 相邻点
    {
        ppGraph[edges[row][1]][pIndegree[edges[row][1]]++] = edges[row][0];
        ppGraph[edges[row][0]][pIndegree[edges[row][0]]++] = edges[row][1];
        pColOfGraph[edges[row][1]]++;
        pColOfGraph[edges[row][0]]++;
    }

    for (row = 0; row <= n - 1; row++) // 入度为1的结点进队
    {
        if (1 == pIndegree[row])
        {
            pQueue[++rear] = row;
        }
    }

    while (2 < n) // 结果只能是1或者2,证明略
    {
        lenghOfQue = rear - front; // 队长
        n -= lenghOfQue; // 更新结点个数
        
        while (lenghOfQue--)
        {   
            curr = pQueue[++front]; // peek & pop

            for (col = 0; col <= pColOfGraph[curr] - 1; col++)
            {
                --pIndegree[curr];
                --pIndegree[ppGraph[curr][col]];

                if (1 == pIndegree[ppGraph[curr][col]])
                {
                    pQueue[++rear] = ppGraph[curr][col]; // push
                }
            }
        }
    }

    *returnSize = rear - front;
    return pQueue + front + 1;
}

时间复杂度:O(N + E);
空间复杂度:O(N)O(N);

2.1.1 该题的设计思路

使用数组(或者哈希表)记录每个节点的颜色: color[node]。颜色可以是 0, 1,或者未着色.搜索节点,考虑图是非连通的情况。对每个未着色节点,从该节点开始深度优先搜索着色。每个邻接点都可以通过当前节点着相反的颜色。如果存在当前点和邻接点颜色相同,则着色失败。使用栈完成深度优先搜索,栈类似于节点的 “todo list”,存储着下一个要访问节点的顺序。在 graph[node] 中,对每个未着色邻接点,着色该节点并将其放入到栈中。

2.1.3 运行结果

2.1.4分析该题目解题优势及难点。

我想本题最大的难度在于非连通图的判定,该解法借助栈对每个需要着色的点进行记录,然后再一一进行遍历,所以本题解最大的优势就是结合了栈对需要进行保存,然后先进后出的遍历。

posted on 2020-05-05 21:30  飞白2020  阅读(203)  评论(0编辑  收藏  举报