• 图总结


    1.思维导图

    2.重要概念的笔记

    一.图的分类

    1.图:是由一个顶点集 V 和一个顶点间的关系集合组成的数据结构。
    2.无向图: 图中顶点之间的边都是无向边。如果任意两个顶点之间都存在边,则称该图为无向完全图,含有n个顶点的无向完全图有n(n-1)/2条边。
    3.有向图: 图中顶点之间的边都是有向边。如果任意两个顶点之间都存在方向互为相反的两条弧,则称该图为有向完全图。含有n个顶点的有向完全图有n(n-1)条边。
    5.稀疏图、稠密图:边或弧的个数 e<nlogn为稀疏图,否则为稠密图。
    5.连通图: 在无向图中,对于图中任意两个顶点都是连通的(顶点i到顶点j有路径).
    6.强连通图: 在有向图中,对于图中任意两个顶点都是连通的。

    二.图的基本概念

    1.度:顶点v关联的边的数目定义为边的度。对于有向图,顶点的度=顶点的入度+顶点的出度。无向图中所有顶点的度之和等于边数的2倍,有向图中所有顶点的入度之和等于所有顶点的出度之和。
    2.简单路径:序列中顶点不重复出现的路径称为简单路径。
    3.简单回路:除了第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路,序列中第一个顶点和最后一个顶点相同的路径,称为简单回路或简单环。
    4.连通图:若图G中任意两个顶点之间都有路径相通,则称此图为连通图;
    5.连通分量:若无向图为非连通图,则图中各个极大连通子图称作此图的连通分量。

    三.图的代码实现

    图的邻接矩阵

    #define MAXVEXNUM 最大顶点个数
    typedef  int ArcCell;
    typedef char VexType;
    typedef struct {
    	VexType vexs[MAXVEXNUM];//点的集合
    	ArcCell arcs[MAXVEXNUM][MAXVEXNUM];//边的集合
    	int vexNum, arcNum;
    }MyGraph;
    

    图的邻接表

    typedef struct ArcNode
    {
        int adjvex;
        int weight;
        struct ArcNode* nextarc;
    }ArcNode;
    typedef struct VNode
    {
        VerTexType data;
        ArcNode* firstarc;
    }VNode;
    typedef struct
    {
        int n, e;
        VNode adjlist[MAXV];
    }Adjraph;
    

    图的邻接矩阵的建立

    void CreateGraphFromConsole(MyGraph& G, int vexNum, int arcNum)
    {
    	G.vexNum = vexNum;
    	G.arcNum = arcNum;
    	//初始化
    	for (int i = 0; i < vexNum; i++)
    	{
    		G.visited[i] = 0;
    		for (int j = 0; j < vexNum; j++)
    		{
    			G.arcs[i][j] = 0;
    		}
    	}
    	printf("Please input %d vex name:
    ", vexNum);
    
    	//cout << "输入点信息" << endl;
    	//点
    	for (int i = 0; i < vexNum; i++)
    	{
    		printf("i=%d  ", i);
    		cin >> G.vexs[i];
    	}
    	//边
    	for (int j = 0; j < arcNum; j++)
    	{
    		char a, b;
    		ArcCell c;
    		cout << "输入x点,y点,及x到y边的值:" << endl;
    		cin >> a >> b >> c;
    		cout << " x=" << a << "," << " y=" << b << "," << " value=" << c << endl;
    		G.arcs[LocateVex(G, a)][LocateVex(G, b)] = c;
    		G.arcs[LocateVex(G, b)][LocateVex(G, a)] = c;
    	}
    }
    int LocateVex(MyGraph& G, VexType value)
    {
    	for (int i = 0; i < G.vexNum; i++)
    	{
    		if (value == G.vexs[i])
    			return i;
    	}
    	return -1;
    }
    

    图的深度遍历DFS

    void DFSTravse(MyGraph& G, int v)    //深度遍历
    {
    	int i;
    	cout << G.vexs[v] << " ";
    	G.visited[v] = 1;
    	for (i = 0; i < G.vexNum; i++)
    	{
    		if (G.arcs[v][i]) {
    			if (G.visited[i] == 0) 
    				DFSTravse(G, i);
    		}
    	}
    }
    

    图的广度遍历BFS

    void BFSTravse(MyGraph& G, int v)    //广度遍历
    {
    	queue<int>q;
    	G.visited[v] = 1;
    	q.push(v);
    	while (!q.empty()) 
    	{
    		int k = q.front();
    		q.pop();
    		cout << G.vexs[k] << " ";
    		for (int i = 0; i < G.vexNum; i++)
    		{
    			if (G.arcs[k][i] != 0 && G.visited[i] == 0){
    				q.push(i);
    				G.visited[i] = 1;
    			}
    		}
    	}
    }
    

    四.最小生成树

    概念

    包含无向连通图G所有n个顶点的极小连通子图称为G的生成树,其中权值之和最小的生成树为最小生成树

    1.普利姆(Prim)算法

    时间复杂度:O(n2),适用于稠密图
    基本思想:
    第一步:取图中任意一个顶点 v 作为生成树的根;
    第二步:往生成树上添加新的顶点 w。在添加的顶点w 和已经在生成树上的顶点v 之间必定存在一条边,并且该边的权值在所有连通顶点 v 和 w 之间的边中取值最小;
    第三步:继续往生成树上添加顶点,直至生成树上含有 n 个顶点为止。

    2.克鲁斯卡尔(Kruskal)算法

    在剩下的所有未选取的边中,找最小边,如果和已选取的边构成回路,则放弃,选取次小边。
    时间复杂度:O(eloge),适用于稀疏图
    基本思想:
    第一步:构造一个只含 n 个顶点的子图 SG;
    第二步:从权值最小的边开始,若它的添加不使SG中产生回路,则在 SG 上加上这条边;
    第三步:如此重复,直至加上 n-1 条边为止。

    五.最短路径

    1.迪杰斯特拉(Dijkstra)算法:

    按路径长度递增次序产生最短路径算法,时间复杂度为O(n3)。
    •把V分成两组
    •⑴S:已求出最短路径的顶点的集合
    •⑵T=V-S:尚未确定最短路径的顶点集合
    •将T中顶点按最短路径递增的次序加入到S中,直到T中没有顶点为止。

    2.弗洛伊德(Floyd)算法

    逐个顶点试探,时间复杂度为O(n3)。
    从vi到vj的所有可能存在的路径中,选出一条长度最短的路径。
    •初始设置一个n阶方阵,令其对角线元素为0,若存在弧<vi,vj>,则对应元素为权值,否则为无穷。
    •逐步试着在原直接路径中增加中间顶点,若加入中间点后路径变短,则修改。否则,维持原值。
    •所有顶点试探完毕,算法结束。

    六.拓扑排序

    拓扑排序的有向图中没有回路。
    AOV-网中不应该出现有向环。
    一个AOV-网的拓扑序列不是唯一的。
    拓扑排序:
    •从有向图中选取一个没有前驱的顶点,并输出之;
    •从有向图中删去此顶点以及所有以它为尾的弧;
    •重复上述两步,直至图空,或者图不空但找不到无前驱的顶点为止。

    七.关键路径

    概念:

    •事件vi的最早发生时间:从开始点v1到vi的最长路径长度。用ve(i)表示.
    •事件vi的最迟发生时间:在不推迟整个工期的前提下,事件vi允许的最晚发生时间。用vl(i)表示。
    •活动ai的最早开始时间:即为ai的弧尾顶点(事件)的最早发生时间。用e(i)表示。
    •活动ai的最迟开始时间:在保证不推迟整个工程的完成时间的前提下,活动ai的最迟开始时间。用l(i)表示。
    •关键活动:l(i) = e(i)的活动。

    算法实现

    •输入e条弧,建立AOE-网。
    •从源点v0出发,令ve[0] = 0, 按拓扑有序求各顶点的最早发生时间vei,如果得到的拓扑序列中顶点个数小于网中顶点数n,说明网中存在环,不能求关键路径,算法终止;否则执行步骤。
    •从汇点vn出发,令vl[n-1] = ve[n-1]; 按逆拓扑有序求其余各顶点的最迟发生时间vli
    •根据各个顶点的ve和vl值,求每条弧s的e(s)和l(s)。若某条弧满足e(s) == l(s),则为关键活动。

    3.疑难问题及解决方案



    这道题的意思就是根据数据创建出一个图,如果图不能连通则输出-1,若可以连通则输出最小生成树的权值之和,最开始会发生一些小意外,一些细节的地方没处理好,比如题目要求的最多的城镇数目小于1000,最初没注意这个导致运行结果最后几个点运行超时,用define设置了最大顶点数后又发现没有错误但运行失败,比对了同学的代码才发现我未对邻接矩阵申请空间导致运行失败,且优化了对检测图是否连通的代码,对一些细节也进行了优化

    #include<iostream>
    using namespace std;
    #define MAXV 2000    //最大顶点数
    #define INF 32767    //表示∞
    
    typedef struct
    {
    	int n, e;
    	int arcs[MAXV][MAXV];
    }MGraph;
    void CreateGraphFromConsole(MGraph* G, int n, int e);
    void prim(MGraph* G, int v);
    int main()
    {
    	int N, M;
    	cin >> N >> M;
    	MGraph* G = new  MGraph;
    	CreateGraphFromConsole(G, N, M);
    	if (M < (N - 1))
    		cout << "-1";
    	else
    		prim(G, 1);
    	return 0;
    }
    void CreateGraphFromConsole(MGraph* G, int n, int e)
    {
    	int i, j, x, y, w;
    	G->n = n;
    	G->e = e;
    	for (i = 1; i <= n; i++)
    		for (j = 1; j <= n; j++)
    			G->arcs[i][j] = INF;
    	for (i = 1; i <= e; i++)
    	{
    		cin >> x >> y >> w;
    		G->arcs[x][y] = G->arcs[y][x] = w;
    	}
    }
    void prim(MGraph* G, int v)
    {
    	int i, j, l, k, sum = 0, min;
    	int lowcost[MAXV] = { 0 }, closedge[MAXV] = { 0 };
    	for (i = 2; i <= G->n; i++)
    	{
    		closedge[i] = v;
    		lowcost[i] = G->arcs[v][i];
    	}
    	for (i = 2; i <= G->n; i++)
    	{
    		min = INF;
    		for (j = 2; j <= G->n; j++) {
    			if (lowcost[j] != 0 && lowcost[j] < min) {
    				min = lowcost[j];
    				k = j;
    			}
    		}
    		sum += min;
    		lowcost[k] = 0;
    		for (l = 2; l < G->n; l++) {
    			if (lowcost[l] != 0 && lowcost[l] > G->arcs[k][l])
    			{
    				lowcost[l] = G->arcs[k][l];
    				closedge[l] = k;
    			}
    		}
    	}
    
    	int flag = 0;
    	for (i = 2; i <= G->n; i++) {
    		if (lowcost[i])
    		{
    			flag = 1;
    			break;
    		}
    	}
    	if (flag)
    		cout << "-1";
    	else
    		cout << sum;
    }
    
  • 相关阅读:
    Linux ifconfig 命令
    linux sed命令就是这么简单
    让博客园博客自动生成章节目录索引
    linux中cat、more、less命令区别详解
    Linux yum源搭建及配置
    关于java中位运算的左移、右移、无符号右移
    一个老话题,short s=s+1的日常
    C语言移位运算符
    关于异或的一些东西和应用
    指针就算指向了常量也不能修改这个常量
  • 原文地址:https://www.cnblogs.com/ssp1781554770/p/12902456.html
Copyright © 2020-2023  润新知