图
图的创建
邻接距阵
思路
结构体中:
有一维顶点数组,保存顶点值
有二维边数组,即距阵(初始化为0–表示不连通)
有int 类型的顶点数,边数
创建顶点数组:
一层for循环遍历储存
创建边数组:
先初始化二维数组为0;
根据传入的两个顶点值,在一维顶点表中找到对应的下标、
两层循环,将二维矩阵有的变为1
代码
#include<stdio.h>
#define MVNum 100
typedef struct {
//顶点表
char vexs[MVNum];
//边表
int arcs[MVNum][MVNum];
//顶点数和边数
int vexnum,arcnum;
}MGraph;
void CreatMGraph(MGraph &G);
void PrintMGgraph(MGraph G);
int main()
{
MGraph G;
CreatMGraph(G);
PrintMGgraph(G);
return 0;
}
//在顶点数组中寻找char 所对应的下标
int locate(MGraph G,char v)
{
for(int i=0;i<G.vexnum;i++)
{
if(G.vexs[i]==v)
{
return i;
}
}
return -1;
}
void CreatMGraph(MGraph &G)
{
char v1,v2;
//输入顶点数与边数
printf("请输入顶点数与边数:\n");
scanf("%d%d",&G.vexnum,&G.arcnum);
getchar();
//输入顶点数组
printf("请输入顶点数组:\n");
for(int i=0;i<G.vexnum;i++)
{
scanf("%c",&G.vexs[i]);
}
//初始化边数组
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
G.arcs[i][j]=0;
}
}
//输入边数组
printf("请输入边数组:\n");
for(int k=0;k<G.arcnum;k++)
{
getchar();
scanf("%c%c",&v1,&v2);
int i=locate(G,v1);
int j=locate(G,v2);
G.arcs[i][j]=1;
G.arcs[j][i]=1;
}
}
void PrintMGgraph(MGraph G)
{
for(int i=0;i<G.vexnum;i++)
{
printf("%c->",G.vexs[i]);
for(int j=0;j<G.vexnum;j++)
{
if(G.arcs[i][j])
printf("%c ",G.vexs[j]);
}
printf("\n");
}
}
邻接表
思路
1.两个结构体
2.储存顶点的Vnode
3.储存每个小结点的arcnode
代码
#include <stdio.h>
#include <stdlib.h>
#define MVNum 100 //最大顶点数
typedef struct ArcNode{ //表结点
int adjvex; //邻接点的位置
struct ArcNode *nextarc; //指向下一个表结点的指针
}ArcNode;
typedef struct VNode{
char data; //顶点信息
ArcNode *firstarc; //指向第一个表结点的指针
}VNode, AdjList[MVNum]; //AdjList表示邻接表类型
typedef struct{
AdjList vertices; //头结点数组
int vexnum, arcnum; //图的当前顶点数和边数
}ALGraph;
void CreatMGraph(ALGraph &G);/* 创建图 */
void printGraph(ALGraph G);/*输出图 */
int main()
{
ALGraph G;
CreatMGraph(G);
printGraph(G);
return 0;
}
void printGraph(ALGraph G)
{
int i;
ArcNode *p;
for(i=0;i<G.vexnum;i++)
{
printf("%c:",G.vertices[i].data);
for(p=G.vertices[i].firstarc;p;p=p->nextarc)
printf(" %c",G.vertices[p->adjvex].data);
printf("\n");
}
}
/* 请在这里填写答案 */
int locate(ALGraph G,char v)//求顶点v的下标
{
for(int i=0;i<G.vexnum;i++)
{
if(v==G.vertices[i].data)
{
return i;
}
}
return -1;
}
void CreatMGraph(ALGraph &G)//创建图G
{
//输入顶点数与边数
scanf("%d %d",&G.vexnum,&G.arcnum);
getchar();
//输入顶点表
for(int i=0;i<G.vexnum;i++)
{
scanf("%c",&G.vertices[i].data);
//初始化表
G.vertices[i].firstarc=NULL;
}
char a,b;
ArcNode *s;
//输入邻接表
for(int k=0;k<G.arcnum;k++)
{
getchar();
scanf("%c%c",&a,&b);
int i=locate(G,a);
int j=locate(G,b);
s=(ArcNode *)malloc(sizeof(ArcNode));
s->adjvex=j;
s->nextarc=G.vertices[i].firstarc;
G.vertices[i].firstarc=s;
s=(ArcNode *)malloc(sizeof(ArcNode));
s->adjvex=i;
s->nextarc=G.vertices[j].firstarc;
G.vertices[j].firstarc=s;
}
}
图的遍历
深度优先遍历
思路:
1.找到一个顶点开始访问
2.访问这个顶点可以访问的下一个顶点继续访问
3.重复以上直至访问完毕
注意:
设置visited[]数组来存放每个结点的访问情况,初始值为0,表示未被访问
代码:
int DFSCount(int v)
{
static int count=1;
int w;
visited[v]=1; //访问当前结点
for(w=0;w<max_matrix;w++)
if(!visited[w] && matrix[v][w]!=0)
{
count++;
DFSCount(w);
}
return count;
}
广度优先遍历
思路:
类似二叉树的层次遍历
开辟一个队列来入队出队模拟访问过程
注意:
设置visited[]数组来存放每个结点的访问情况,初始值为0,表示未被访问
简便方法处理队列,使用一维数组代替队列,记录队首队尾指针
代码
int BFS(ALGraph G,int v) //以顶点v开始进行广度优先遍历
{
int u;
ArcNode *p;
SqQueue Q; //创建辅助队列
InitQueue(Q); //初始化辅助队列
printf("广度优先遍历序列:%c ",G.vertices[v].data);
visited[v]=1; //访问顶点v
EnQueue(Q,v); //顶点v入队
while(!QueueEmpty(Q)) //队列为空时结束循环
{
DeQueue(Q,u) ; //顶点u出队
p=G.vertices[u].firstarc;
while(p)
{
if(!visited[p->adjvex] ) //若存在边且顶点w未访问
{
printf("%c ",G.vertices[p->adjvex].data);
visited[p->adjvex]=1; //访问顶点w
EnQueue(Q,p->adjvex); //顶点w入队
}
p=p->nextarc;
}
}
printf("\n\n");
return 0;
}
最小生成树
实现原理:
选取原图中权值之和最小的n-1条边,使得n个顶点恰好连通
n个顶点+n-1条边
实现算法
prim算法
思路–找点法
1.任意从一个顶点u开始,将u加入集合,表示已经访问
2.选择这个顶点所连边中最小的边,另一端顶点放入集合,表示已经访问
3.重复2,直到所有顶点访问完毕
代码
1.0---表示已经访问
2.parent[k]----表示k节点的最小边的前驱节点
void prim(MGraph g,int v) //从顶点V0出发,按普里姆算法构造联通网G的最小生成树
{
int Vlength[MAXV];//记录权值
int i, j, k;
int parent[MAXV];//记录边的起始节点
int min;
for(i=0;i<g.vexnum;i++)
{
Vlength[i]=g.edges[v][i];
parent[i]=v;
}
Vlength[v]=0;//标记已经访问这个下标对应的节点
for(i=1;i<g.vexnum;i++)
{
min=INF;
for(j=0;j<g.vexnum;j++)
{
if(Vlength[j]!=0&&Vlength[j]<min)
{
min=Vlength[j];
k=j;
}
}
printf("结点<%s~~~~~~~%s) 边的权值为: %d\n",g.vexs[parent[k]],g.vexs[k],min);
Vlength[k]=0;//标记已经访问k节点
//从k节点开始继续寻找
for(j=0;j<g.vexnum;j++)
{
if(Vlength[j]!=0 && g.edges[k][j]<Vlength[j])
{
Vlength[j]=g.edges[k][j];
parent[j]=k;
}
}
}
}
kruskal算法
思路–找边法
1.找到最小权值的边
2.将此边的后驱节点划分到与前驱节点的同一连通分量中
3.找后驱节点所处的与其在不同连通分量的并且权值最小的边,
4.重复2,3,直到所有顶点都处于同一连通分量
代码
1.vset[]--判断两个点是否连通
2.E[]--- 从小到大储存边,数据类型是结构体:边起始点,边终止点,边权值
typedef struct node
{
int u; //边的起始顶点
int v; //边的终止顶点
int w; //边的权值
}Edge;
void sort(MGraph G,Edge E[])
{
Edge t;
int i,j;
for(i=0;i<G.arcnum-1;i++)
for(j=0;j<G.arcnum-i-1;j++)
if(E[j].w>E[j+1].w)
{
t=E[j];
E[j]=E[j+1];
E[j+1]=t;
}
}
void kruskal(MGraph G)
{
int i,j,sn1,sn2,k;
int vset[MAXV]; //辅助数组。判定两个顶点是否连通
Edge E[MAXV]; //存放全部的边
k=0; //E数组的下标从0開始
for (i=0;i<G.vexnum;i++)
{
for (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++;
}
}
}
sort(G,E); //堆排序,按权值从小到大排列
for (i=0;i<G.vexnum;i++) //初始化辅助数组
{
vset[i]=i; //所在集合
}
k=1; //生成的边数,最后要刚好为总边数
j=0; //E中的下标
while (k<G.vexnum)
{
sn1=vset[E[j].u];
sn2=vset[E[j].v]; //得到两顶点属于的集合编号
if (sn1!=sn2) //不在同一集合编号内的话,把边增加最小生成树
{
printf("边:%s ---> %s, 权值:%d\n",G.vexs[E[j].u],G.vexs[E[j].v],E[j].w);
k++;
vset[E[j].v]=sn1;
}
j++;
}
}
最短路径
原理解释
图中,两个节点中的所有路径中权值最小的一条路径
Dijaksta算法
思路
代码
Vertex FindMinDist( MGraph Graph, int dist[], int collected[] )
//dist[]某顶点到其他顶点的权值,collected[]-false--表示这个顶点还没有找到最短路径
{
/* 返回未被收录顶点中dist最小者 */
Vertex MinV, V;
int MinDist = INFINITY;
for (V=0; V<Graph.Nv; V++) {
if ( collected[V]==FALSE && dist[V]<MinDist) {
/* 若V未被收录,且dist[V]更小 */
MinDist = dist[V]; /* 更新最小距离 */
MinV = V; /* 更新对应顶点 */
}
}
if (MinDist < INFINITY) /* 若找到最小dist */
return MinV; /* 返回对应的顶点下标 */
else return ERROR; /* 若这样的顶点不存在,返回错误标记 */
}
bool Dijkstra( MGraph Graph, int dist[], Vertex S )
{
int collected[MAX_VERTEX_NUM];
Vertex V, W;
/* 初始化:此处默认邻接矩阵中不存在的边用INFINITY表示 */
for ( V=0; V<Graph.Nv; V++ ) {
dist[V] = Graph.arcs[S][V];
collected[V] = FALSE;
}
dist[S]=0;
collected[S] =TRUE;
while(1)
{
V=FindMinDist(Graph,dist,collected);//找到与s相连接的权值最短的点
if(V==ERROR)
break;
collected[V] =TRUE;//标记此点已经找到
for(W=0;W<Graph.Nv;W++)
if(Graph.arcs[V][W]<INFINITY && collected[W]==FALSE && dist[V]+Graph.arcs[V][W]<dist[W])
dist[W]=dist[V]+Graph.arcs[V][W];
}
return OK; /* 算法执行完毕,返回正确标记 */
}