• DS博客作业04--图


    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分析该题目解题优势及难点。

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

  • 相关阅读:
    windows Visual Studio 2017 编译 HEVC cmake-3.8.1-win64-x64.msi 下载
    DirectShow
    IDA反汇编学习
    .NET dnSpy 程序集编辑器,反编译器和调试器
    Win7如何自定义鼠标右键菜单 添加新建PowerPoint文档
    Win7如何自定义鼠标右键菜单 添加新建EXCEL文档
    推荐电视剧 大秦帝国之裂变 2008
    推荐电视剧 大明王朝1566 2007
    C#如何生成release版本的程序,生成debug版本的程序
    Solidworks安装完成提示failed to load slderresu.dll怎么办
  • 原文地址:https://www.cnblogs.com/my-sky-2019/p/12832904.html
Copyright © 2020-2023  润新知