zoukankan      html  css  js  c++  java
  • 图总结

    一、思维导图

    二、重要概念的笔记

    图的邻接矩阵的相关算法设计

    邻接矩阵存储结构及创建图

    #include<iostream>
    #include<string>
    #include<queue>
    #include <stdio.h>
    #include <stdlib.h>
    using namespace std;
    #define MAXVEXNUM 1000
    #define  MAXV  20
    #define INF 0x7fffffff
    typedef  int ArcCell;
    typedef int VexType;
    typedef struct {
    	VexType vexs[MAXVEXNUM];//点的集合
    	ArcCell arcs[MAXVEXNUM][MAXVEXNUM];//边的集合
    	int vexNum, arcNum;
    }MyGraph;
    int visited[100];
    int flag = 0; 
    int LocateVex(MyGraph*& G, VexType value)
    {
    	for (int i = 0; i < G->vexNum; i++)
    	{
    		if (value == G->vexs[i])
    			return i;
    	}
    	return -1;
    }
    void CreateAdj2(MyGraph*& G, int n, int e) {
    	G = new MyGraph;
    	G->vexNum = n;
    	G->arcNum = e;
    	for (int i = 1; i <= n; i++) {
    		for (int j = 1; j <= n; j++) {
    			G->arcs[i][j] = 0;
    		}
    	}
    	for (int i = 1; i <= n; i++) {
    		G->vexs[i] = i;
    	}
    	for (int j = 1; j <= G->arcNum; j++) {
    		int x, y; int value;
    		//cout << "请输入x点、y点的值及x到y的边的值:" << endl;
    		cin >> x; cin >> y; 
    		//cout << "x=" << x << " "; cout << " y=" << y << " "; cout << " value=" << value << endl;
    		G->arcs[x][y] = 1;
    		G->arcs[y][x] = 1;
    	}
    }
    

    邻接矩阵的DFS

    void DFS(MyGraph* G, int i) {
    	visited[i] = 1;
    	printf("%c ", G->vexs[i]);
    	for (int j = 0; j < G->vexNum; j++) {
    		if (G->arcs[i][j]!=0 && !visited[j]) {
    			DFS(G, j);
    		}
    	}
    }
    void DFSTraverse(MyGraph* G) {
    	for (int i = 0; i < G->vexNum; i++) {
    		visited[i] = 0;
    	}
    	for (int i = 0; i < G->vexNum; i++) {
    		if (!visited[i]) {
    			DFS(G, i);
    		}
    	}
    }
    

    邻接矩阵的BFS

    void BSTTraverse(MyGraph* G) {
    	queue<int>Q;
    	for (int i = 0; i < G->vexNum; i++) {
    		visited[i] = 0;
    	}
    	for (int i = 0; i < G->vexNum; i++) {
    		if (!visited[i]) {
    			visited[i] = 1;
    			printf("%c ", G->vexs[i]);
    			Q.push(i);
    			while (!Q.empty()) {
    				i = Q.front();
    				Q.pop();
    				for (int j = 0; j < G->vexNum; j++) {
    					if (G->arcs[i][j]!=0 && !visited[j]) {
    						visited[j] = 1;
    						printf("%c ", G->vexs[j]);
    						Q.push(j);
    					}
    				}
    			}
    		}
    	}
    }
    

    输出邻接矩阵

    void printMGraph(MyGraph*& G)
    {
    	for (int i = 0; i < G->vexNum; i++)
    	{
    		for (int j = 0; j < G->vexNum; j++)
    		{
    			cout << G->arcs[i][j] << "	";
    		}
    		cout << endl;
    	}
    }
    

    利用邻接矩阵判断两个顶点之间有无路径

    存在路径则返回true,不存在则返回false

    bool printPathBetweenVex(MyGraph*& G, char startV, char endV) {
    	queue<int>Q;
    	char a[100]; int k = 0;
    	for (int i = 0; i < G->vexNum; i++) {
    		visited[i] = 0;
    	}
    	int i = LocateVex(G, startV);
    	if (i == -1) {
    		return false;
    	}
    	for (; i < G->vexNum; i++) {
    		if (!visited[i]) {
    			visited[i] = 1;
    			a[k] = G->vexs[i]; 
    			k++;
    			//printf("%c ", G->vexs[i]);
    			Q.push(i);
    			while (!Q.empty()) {
    				i = Q.front();
    				Q.pop();
    				for (int j = 0; j < G->vexNum; j++) {
    					if (G->arcs[i][j] != 0 && !visited[j]) {
    						visited[j] = 1;
    						a[k] = G->vexs[j];
    						k++;
    						//printf("%c ", G->vexs[j]);
    						Q.push(j);
    					}
    				}
    			}
    		}
    	}
    	for (int i = 0; i < k; i++) {
    		if (a[i] == endV) {
    			return true;
    		}
    	}
    	return false;
    }
    

    图的邻接表的相关算法设计

    邻接表存储结构及创建图

    #define  MAXV  20
    #include <stdio.h>
    #include <stdlib.h>
    #include <iostream>
    #include<queue>
    using namespace std;
    typedef struct ANode
    {
    	int adjvex;			//该边的终点编号
    	struct ANode* nextarc;	//指向下一条边的指针
    	int info;	//该边的相关信息,如权重
    } ArcNode;				//边表节点类型
    typedef int Vertex;
    typedef struct Vnode
    {
    	Vertex data;			//顶点信息
    	ArcNode* firstarc;		//指向第一条边
    } VNode;				//邻接表头节点类型
    typedef VNode AdjList[MAXV];
    typedef struct
    {
    	AdjList adjlist;		//邻接表
    	int n, e;		//图中顶点数n和边数e
    } AdjGraph;
    int visited[MAXV];
    int flag = 0;
    void CreateAdj1(AdjGraph*& G, int n, int e) {//无向图的创建
    	ArcNode* p;
    	G = new AdjGraph;
    	for (int i = 1; i <= n; i++) {
    		G->adjlist[i].firstarc = NULL;
    	}
    	int x, y;
    	for (int i = 1; i <= e; i++) {
    		cin >> x >> y;
    		p = new ArcNode;
    		p->adjvex = y;
    		p->nextarc = G->adjlist[x].firstarc;
    		G->adjlist[x].firstarc = p;
    
    		p = new ArcNode;
    		p->adjvex = x;
    		p->nextarc = G->adjlist[y].firstarc;
    		G->adjlist[y].firstarc = p;
    	}
    	G->n = n;
    	G->e = e;
    }
    

    邻接表的DFS

    void DFS(AdjGraph* G, int v) {
    	for (int i = 1; i <= G->n; i++) visited[i] = 0;
    	ArcNode* p;
    	flag++;
    	if (flag == 1) {
    		printf("%d", v);
    	}
    	else {
    		printf(" %d", v);
    	}
    	visited[v] = 1;
    	p = G->adjlist[v].firstarc;
    	while (p != NULL) {
    		if (visited[p->adjvex] == 0) {
    			DFS(G, p->adjvex);
    		}
    		p = p->nextarc;
    	}
    }
    

    邻接表的BFS

    void BFS(AdjGraph* G, int v) {
    	for (int i = 1; i <= G->n; i++) visited[i] = 0;
    	ArcNode* p;
    	int t;
    	queue<int>Q;
    	printf("%d", v);
    	visited[v] = 1;
    	Q.push(v);
    	while (!Q.empty()) {
    		t = Q.front();
    		Q.pop();
    		p = G->adjlist[t].firstarc;
    		while (p != NULL) {
    			if (visited[p->adjvex] == 0) {
    				printf(" %d", p->adjvex);
    				visited[p->adjvex] = 1;
    				Q.push(p->adjvex);
    			}
    			p = p->nextarc;
    		}
    	}
    }
    

    普利姆(Prim)算法

    基本思路:对于图G而言,V是所有顶点的集合;现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T存放G的最小生成树中的边。 从所有uЄU,vЄ(V-U) (V-U表示除去U的所有顶点)的边中选取权值最小的边(u, v),将顶点v加入集合U中,将边(u, v)加入集合T中,如此不断重复,直到U=V为止,最小生成树构造完毕,这时集合T中包含了最小生成树中的所有边。
    过程图解:



    算法实现:
    代码出自:https://www.cnblogs.com/yx1999/p/10357626.html

    void MiniSpanTree_Prim(MGragh G)
    {
        int mini,i,j,k;
        int adjvex[MAXVEX]; //保存相关顶点下标
        int lowcost[MAXVEX]; //保存相关顶点间边的权值
        lowcost[0] = 0;//这里把第0位的权值置0表示v0已加入生成树
        //ps:lowcost[i] = 0 表示i那个下标的顶点加入生成树
        adjvex[0] = 0; //初始化第一个顶点的下标为0
        for(i = 0; i < G.numVertexes; i++)
        {
            lowcost[i] = G.arc[0][i];//将vo相关顶点的权值存入lowcost数组
            adjvex[i] = 0;//置所有下标为v0
        }
        for(i = 1; i < G.numVertexes; i++) //最小生成树开始辽
        {
            mini = INFITINY; //先把权值的最小值置为无限大
            j = 1;
            k = 0;
            while(j < G.numVertexes)
            {
                if(lowcost[j] != 0 && lowcost[j] < mini)//判断并向lowcost中添加权值
                {
                    mini = lowcost[j];
                    k = j;
                }
                j++;
            }
            printf("(%d %d)",lowcost[k],k);
            lowcost[k] = 0;//置0表示这个定点已经完成任务,找到最小权值分支
            for(j = 1; j < G.numVertexes; j++)
            {
                if(lowcost[j] != 0 && G.arc[k][j] < lowcost[j])
                {
                    lowcost[j] = G.arc[k][j];
                    adjvex[j] = k;
                }
            }
        }
    }
    

    克鲁斯卡尔(Kruskal)算法

    基本思路:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路。 首先构造一个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止。
    对比普里姆和克鲁斯卡尔算法,克鲁斯卡尔算法主要针对边来展开,边数少时效率比较高,所以对于稀疏图有较大的优势;而普里姆算法对于稠密图,即边数非常多的情况下更好一些。
    过程图解:

    算法实现:
    代码出自:https://blog.csdn.net/jnu_simba/article/details/8870481

    void kruskal(Graph G)
    {
        int i,m,n,p1,p2;
        int length;
        int index = 0;          // rets数组的索引
        int vends[MAX]={0};     // 用于保存"已有最小生成树"中每个顶点在该最小树中的终点。
        EData rets[MAX];        // 结果数组,保存kruskal最小生成树的边
        EData *edges;           // 图对应的所有边
    
        // 获取"图中所有的边"
        edges = get_edges(G);
        // 将边按照"权"的大小进行排序(从小到大)
        sorted_edges(edges, G.edgnum);
    
        for (i=0; i<G.edgnum; i++)
        {
            p1 = get_position(G, edges[i].start);   // 获取第i条边的"起点"的序号
            p2 = get_position(G, edges[i].end);     // 获取第i条边的"终点"的序号
    
            m = get_end(vends, p1);                 // 获取p1在"已有的最小生成树"中的终点
            n = get_end(vends, p2);                 // 获取p2在"已有的最小生成树"中的终点
            // 如果m!=n,意味着"边i"与"已经添加到最小生成树中的顶点"没有形成环路
            if (m != n)
            {
                vends[m] = n;                       // 设置m在"已有的最小生成树"中的终点为n
                rets[index++] = edges[i];           // 保存结果
            }
        }
        free(edges);
    
        // 统计并打印"kruskal最小生成树"的信息
        length = 0;
        for (i = 0; i < index; i++)
            length += rets[i].weight;
        printf("Kruskal=%d: ", length);
        for (i = 0; i < index; i++)
            printf("(%c,%c) ", rets[i].start, rets[i].end);
        printf("
    ");
    }
    

    狄克斯特拉(Dijkstra)算法

    代码出自:https://blog.csdn.net/qq_16261421/article/details/106005266?fps=1&locationNum=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;                		//s[]置空
    		if (g.edges[v][i]<INF)		//路径初始化
    			path[i]=v;
    		else
    		    path[i]=-1;
    	}
    	s[v]=1;path[v]=0;        		//源点编号v放入s中
    	for (i=0;i<g.n;i++)    			//循环直到所有顶点的最短路径都求出
    	{	
    		mindis=INF;					//mindis置最小长度初值
    		for (j=0;j<g.n;j++)     	//选取不在s中且具有最小距离的顶点u
    			if (s[j]==0 && dist[j]<mindis) 
    			{ 	
    				u=j;
    				mindis=dist[j];	
    			}
    		s[u]=1;               		//顶点u加入s中
    		for (j=0;j<g.n;j++)     	//修改不在s中的顶点的距离
    			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;
    				}  
    	}
    	Dispath(dist,path,s,g.n,v);  	//输出最短路径
    }
    

    三、疑难问题及解决方案

    PTA 7-4 公路村村通


    代码:

    #include<iostream>
    using namespace std;
    #define MAXNUM 1001
    #define INF 0X7fffffff
    typedef struct {
    	int edges[MAXNUM][MAXNUM];
    	int vexs[MAXNUM];
    	int vexNum, arcNum;
    }MyGraph;
    int visited[MAXNUM] = { 0 };
    void Prim(MyGraph* G, int v) {
    	int lowcast[MAXNUM];
    	int lowcost[MAXNUM];
    	int MIN;
    	int closet[MAXNUM], i, j, k;
    	for (i = 1; i <= G->vexNum; i++) {
    		lowcost[i] = G->edges[v][i];
    		closet[i] = v;
    	}
    	for (i = 1; i < G->vexNum; i++) {
    		MIN = INF;
    		for (j = 1; j <= G->vexNum; j++) {
    			if (lowcost[j] != 0 && lowcost[j] < MIN) {
    				MIN = lowcost[j];
    				k = j;
    			}
    		}
    		lowcast[i] = lowcost[k];
    		lowcost[k] = 0;
    		for (j = 1; j <= G->vexNum; j++) {
    			if (lowcost[j] != 0 && G->edges[k][j] < lowcost[j]) {
    				lowcost[j] = G->edges[k][j];
    				closet[j] = k;
    			}
    		}
    	}
    	int result = 0;;
    	for (i = 1; i < G->vexNum; i++) {
    		result += lowcast[i];
    	}
    	printf("%d", result);
    }
    void IfConnected(MyGraph* G,int v) {
    	visited[v] = 1;
    	for (int i = 1; i <= G->vexNum; i++) {
    		if (visited[i] == 0 && G->edges[v][i] != 0 && G->edges[v][i] != INF) {
    			IfConnected(G, i);
    		}
    	}
    }
    int main() {
    	int N, M;
    	cin >> N >> M;
    	
    	MyGraph* G;
    	G = new MyGraph;
    	int i, j, x, y, value;
    	G->vexNum = N; G->arcNum = M;
    	for (i = 1; i <= N; i++) {
    		G->vexs[i] = i;
    	}
    	for (i = 1; i <= N; i++) {
    		for (j = 1; j <= N; j++) {
    			if (i == j) G->edges[i][j] = 0;
    			else G->edges[i][j] = INF;
    		}
    	}
    	for (i = 1; i <= M; i++) {
    		cin >> x >> y >> value;
    		G->edges[x][y] = value;
    		G->edges[y][x] = value;
    	}
    	if (M < N - 1) {
    		printf("-1");
    		return 0;
    	}
    	IfConnected(G, 1);
    	for (i = 1; i <= N; i++) {
    		if (visited[i] == 0) {
    			cout << "-1";
    			return 0;
    		}
    	}
    	Prim(G, 1);
    	return 0;
    }
    

    其他问题

    关于本章图的算法,我们已经学了普利姆算法、克鲁斯卡尔算法、Dijkstra算法、Floyd算法、拓扑排序等等,虽然每种算法各有其对应的问题,但是有时候普利姆算法、克鲁斯卡尔算法和Dijkstra算法会弄混,做题目做着做着就搞混在一起了。

  • 相关阅读:
    2017.11.20 事务
    Linux常用指令
    11.17 知识整理
    不太熟的sql语句
    MySQL关联查询
    2017.11.09 vi编辑器指令
    Linux操作指令
    线程安全,同步锁(同步方法和同步代码)
    多线程
    序列化和反序列化
  • 原文地址:https://www.cnblogs.com/cjt0722/p/12904665.html
Copyright © 2011-2022 走看看