一、图的邻接矩阵存储方法
设 G = ( V, E ) 是含有n(n>0)个顶点的图,各顶点的编号为 0~(n-1) ,则 G 的邻接矩阵数组 A 是 n 阶方阵,其定义如下:
对于无向图:
对于有向图:
图的完整邻接矩阵类型的声明如下:
#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");
}
}
}
}