图知识总结(二):图的邻接矩阵存储方法及相关算法设计

一、图的邻接矩阵存储方法

设 G = ( V, E ) 是含有n(n>0)个顶点的图,各顶点的编号为 0~(n-1) ,则 G 的邻接矩阵数组 A 是 n 阶方阵,其定义如下:

对于无向图:A[i][j]=\left\{\begin{matrix} 1 & (i,j)or<i,j>\in E(G)\\ 0 & others \end{matrix}\right.

对于有向图:A[i][j]=\left\{\begin{matrix} w_{ij} & i\neq j,(i,j)or<i,j>\in E(G) \\ 0 & i=j\\ \infty & others \end{matrix}\right.

图的完整邻接矩阵类型的声明如下:

#define MAXV<最大顶点个数>
#define INF 32767             //定义∞

//定义顶点的类型
typedef struct{
    int no;                   //顶点的编号
    InfoType info;            //顶点的其他信息
} VertexType;

//定义完整的图邻接矩阵类型
typedef struct{
    int edges[MAXV][MAXV];    //邻接矩阵数组
    int n,e;                  //顶点数,边数
    VertexType vexs[MAXV];    //存放顶点信息
} MatGraph;

以下算法设计中,默认MatGraph中VertexType类型的vexs数组元素顶点编号从0开始,即与存储编号相同。

二、基本运算算法

(一)创建图

void CreateMat(MatGraph *&G){
	G=(MatGraph *)malloc(sizeof(MatGraph));
	cin>>G->n>>G->e;//输入顶点数和边数
	for(int i=0;i<G->n;i++){
		cin>>G->vexs[i].no>>G->vexs[i].info;//输入顶点编号和其他信息 
	}
	for(int i=0;i<G->n;i++){
		for(int j=0;j<G->n;j++){
			if(i==j)
				G->edges[i][j]=0;
			else
				G->edges[i][j]=INF; 
		}//初始化邻接矩阵
	}
	for(int i=0;i<G->e;i++){
		int v1,v2,weight;
		cin>>v1>>v2>>weight;//输入顶点和权值 
		G->edges[v1][v2]=weight;
		G->edges[v2][v1]=weight;//存储边 
	}
}

(二)输出图

void DispMat(MatGraph *G){
	for(int i=0;i<G->n;i++){
		cout<<G->vexs[i].no<<" "<<G->vexs[i].info<<endl;//输出顶点信息 
	}
	for(int i=0;i<G->n;i++){
		for(int j=i+1;j<G->n;j++){
			if(G->edges[i][j]!=INF)
				printf("(%d,%d):%d\n",i,j,G->edges[i][j]);//输出边信息 
		}
	} 
}

(三)销毁图

void DestroyMat(MatGraph *&G){
	free(G);
}

三、图的遍历

(一)深度优先搜索(DFS)

bool visited[MAXV];//标记是否被遍历过
void DFS(MatGraph *G,int pos){
    cout<<G->vexs[pos].no<<" ";
    visited[pos]=true;
    for(int i=0;i<G->n;i++){
        if(G->edges[pos][i]>0&&!visited[i]){
        	DFS(G,i);//边存在且顶点未被遍历过则遍历该顶点
		}
	}
}
void DFSTraverse(MatGraph *G){
	for(int i=0;i<G->n;i++){
		visited[i]==false;
	}//初始化,清空原有遍历标记
	for(int i=0;i<G->n;i++){
		if(!visited[i])
            DFS(G,i);
	}       
}

(二)广度优先搜索(BFS)

bool visited[MAXV];//标记是否被遍历过
queue<int> q;
void BFS(MatGraph *G, int pos){
    if(!visited[pos])
        cout<<G->vexs[pos].no<<" ";
    visited[pos]=1;
    for(int i=0;i<G->n;i++){
        if(G->edges[pos][i]>0&&!visited[i])
            q.push(i);//边存在且顶点未被遍历过则放进队列
    }
    if(!q.empty()){
        pos=q.front();
        q.pop();
        BFS(G,pos);//上一层遍历结束后依次对队列内顶点遍历下一层
    }
}
void BFSTraverse(MatGraph *G){
    for(int i=0;i<G->n;i++){
		visited[i]=false;
	}//初始化,清空原有遍历标记
	for(int i=0;i<G->n;i++)
        if(!visited[i])
            BFS(G,i);
}

四、最小生成树

(一)Prim算法

void Prim(MatGraph *G,int v){
	int closest[MAXV];//存储剩余顶点对应的最小边的确定集合顶点
	int lowcost[MAXV];//存储closest对应最小边的权值
	for(int i=0;i<G->n;i++){
		lowcost[i]=G->edges[v][i];
		closest[i]=v;
	}//给lowcost[]和closest[]设置初值
	for(int i=1;i<G->n;i++){
		int k;//记录最近顶点的编号 
		int mindist=INF;
		for(int j=0;j<G->n;j++){
			if(lowcost[j]!=0&&lowcost[j]<mindist){
				mindist=lowcost[j];
				k=j;
			}
		}//在剩余顶点中找出离确定集合中最近的顶点 
		printf("(%d,%d):%d\n",closest[k],k,mindist);
		lowcost[k]=0;//标记k已经加入确定集合 
		for(int j=0;j<G->n;j++){
			if(lowcost[j]!=0&&G->edges[k][j]<lowcost[j]){
				lowcost[j]=G->edges[k][j];
				closest[j]=k;
			}
		}//对剩余顶点中的两个数组进行调整 
	} 
} 

(二)Kruskal算法

typedef struct{
	int u,v,w;//边的起始顶点、终止顶点、权值 
} Edge;
void InsertSort(Edge E[],int n){
	for(int i=1;i<n;i++){
		if(E[i].w<E[i-1].w){
			Edge temp=E[i];
			int j=i-1;
			do{
				E[j+1]=E[j];
				j--;
			}
			while(j>=0&&E[j].w>temp.w);
			E[j+1]=temp;
		}
	}
}
void Kruskal(MatGraph *G){
	int vset[MAXV];
	Edge E[MAXE];
	int k=0,j=0;
	for(int i=0;i<G->n;i++){
		for(int j=0;j<=i;j++){
			if(G->edges[i][j]!=0&&G->edges[i][j]!=INF){
				E[k].u=i;
				E[k].v=j;
				E[k].w=G->edges[i][j];
				k++;
			}
		}
	}
	InsertSort(E,G->e);
	for(int i=0;i<G->n;i++){
		vset[i]=i;
	}
	k=1;
	while(k<G->n){
		int u1=E[j].u,v1=E[j].v;
		int sn1=vset[u1],sn2=vset[v1];
		if(sn1!=sn2){
			printf("(%d,%d):%d\n",u1,v1,E[j].w);
			k++;
			for(int i=0;i<G->n;i++){
				if(vset[i]==sn2){
					vset[i]=sn1;
				}
			}
		}
		j++;
	}
} 

五、最短路径

(一)Dijkstra算法

void Dispath(MatGraph *G,int dist[],int path[],bool S[],int v);
void Dijkstra(MatGraph *G,int v){
	int dist[MAXV],path[MAXV];
	bool S[MAXV];//标识顶点在S中或U中 
	for(int i=0;i<G->n;i++){
		dist[i]=G->edges[v][i];
		S[i]=false;
		if(G->edges[v][i]<INF){
			path[i]=v;
		}
		else {
			path[i]=-1;
		}
	}//对距离和路径进行初始化
	S[v]=true; 
	path[v]=0;
	for(int i=0;i<G->n-1;i++){
		int mindist=INF,u;
		for(int j=0;j<G->n;j++){
			if(S[j]==0&&dist[j]<mindist){
				u=j;
				mindist=dist[j];
			}
		}//选取不在S中且具有最小最短路径长度的顶点u 
		S[u]=true;
		for(int j=0;j<G->n;j++){
			if(S[j]==false){
				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;
				}
			}
		}//修改不在S中的顶点的最短路径 
	}
	Dispath(G,dist,path,S,v);//输出最短路径 
} 
void Dispath(MatGraph *G,int dist[],int path[],bool S[],int v){
	int apath[MAXV],d;//存放一条逆向最短路径及其顶点个数 
	for(int i=0;i<G->n;i++){
		int k;
		if(S[i]==true&&i!=v){
			printf("从顶点%d到顶点%d的路径长度为:%d\t路径为:",v,i,dist[i]);
			d=0;
			apath[d]=i;
			k=path[i];
			if(k==-1){
				printf("无路径\n");
			}
			else {
				while(k!=v){
					d++;
					apath[d]=k;
					k=path[k];
				}
				d++;
				apath[d]=v;
				printf("%d",apath[d]);
				for(int j=d-1;j>=0;j--){
					printf(",%d",apath[j]);
				}
				printf("\n");
			}
		}
	} 
}

(二)Floyd算法

//Floyd算法 
void Dispath(MatGraph *G,int A[][MAXV],int path[][MAXV]);
void Floyd(MatGraph *G){
	int A[MAXV][MAXV],path[MAXV][MAXV];
	for(int i=0;i<G->n;i++){
		for(int 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;
			}
			else {
				path[i][j]=-1;
			}
		}
	}
	for(int k=0;k<G->n;k++){
		for(int i=0;i<G->n;i++){
			for(int 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];
				}
			}
		}
	}
	Dispath(G,A,path);//输出最短路径
}
void Dispath(MatGraph *G,int A[][MAXV],int path[][MAXV]){
	int apath[MAXV],d;//存放一条逆向最短路径及其顶点个数
	for(int i=0;i<G->n;i++){
		for(int j=0;j<G->n;j++){
			int k;
			if(A[i][j]!=INF&&i!=j){
				printf("从顶点%d到顶点%d的路径长度为:%d\t路径为:",i,j,A[i][j]);
				k=path[i][j];
				d=0;
				apath[d]=j;
				while(k!=i){
					d++;
					apath[d]=k;
					k=path[i][k];
				}
				d++;
				apath[d]=i;
				printf("%d",apath[d]);
				for(int s=d-1;s>=0;s--){
					printf(",%d",apath[s]);
				}
				printf("\n");
			}
		}
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

STRANGEX-03

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值