使用邻接矩阵建立图
图的建立及相关算法
头文件
#include "Queue.cpp"//自己编写队列文件
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
所需函数
PtrGraph initGraphWithoutEdge(int VertexNum);//初始化一个有顶点但没有边的图
void insertEdge(PtrGraph pGraph, PtrEdge pEdge);//将边插入图中(修改邻接矩阵)
PtrGraph buildGraph(void); //创建有边和节点的图
void BFS(PtrGraph pGraph, Vertex V);//广度优先搜索(利用队列实现),从V节点开始
void visitNode(Vertex V);//访问V节点
bool isEdge(PtrGraph pGraph, Vertex W, Vertex V);//判断<W,V>是不是图中的边
void DFS(PtrGraph pGraph, Vertex V);//深度优先搜索,从V节点开始
void DFStraversal(PtrGraph pGraph, Vertex V, int* visited);//深度优先搜索的内部函数,从V节点开始,visited是已被访问的节点的数组
void unweigtedShortestPath(PtrGraph pGraph, Source S, Vertex* path, int* distance);//无权图的单源最短路算法
//S是源点,path记录到顶点的要经过的顶点,distance记录到顶点的最短路径
void Dijkstra(PtrGraph pGraph, Source S, Vertex* path, int* distance);//单源加权图,所有权都为正
void Floyd(PtrGraph pGraph, Vertex path[MAX_SIZE][MAX_SIZE], int distance[MAX_SIZE][MAX_SIZE]);//多源加权图最短路径
Vertex findMinDist(int* dist, int length, int* collected);//寻未被搜集中的最小值
int* newZeroArray(int length);
int* newConstantArray(int length, int val);//创建一个长度为length,值都为val的数组
void showArray(int* arr, int length); //输出数组中的所有元素
定义图,边的结构
//使用邻接矩阵建立图
constexpr auto MAX_SIZE = 10; //顶点的最大数目
constexpr auto INFINITY = 100; //定义无穷大
typedef int WeightType; //图的权重为int型
typedef int Vertex, Source; //抽象图中的边的类型
typedef struct { //图结构
int Nv;//顶点数目
int Ne;//边的数目
WeightType AdjMatrix[MAX_SIZE][MAX_SIZE];//邻接矩阵(对加权图元素表示边的权重)
//DataType Date[MAX_SIZE]; //顶点的数据类型
}Graph, * PtrGraph;
typedef struct {//边要包括起点和终点,以及边自身的权重
Vertex V1;
Vertex V2;
WeightType Weight;
}Edge, * PtrEdge;
建立图的方式
先创建顶点,再插入边,具体实现方式:
/*initGraphWithoutEdge函数 和 insertEdge函数 需要根据图的具体类型改变*/
PtrGraph initGraphWithoutEdge(int vertexNum) {//初始化一个有顶点但没有边的图
PtrGraph pGraph = (PtrGraph)malloc(sizeof(Graph));
if (pGraph == NULL) {
printf("分配内存失败!\n");
exit(-1);
}
else {
pGraph->Nv = vertexNum;
pGraph->Ne = 0;
for (Vertex V = 0; V < vertexNum; V++)
for (Vertex W = 0; W < vertexNum; W++) {
pGraph->AdjMatrix[V][W] = INFINITY;//加权图:INFINITY,但对角线上都是0
//无加权图:0
if (W == V)
pGraph->AdjMatrix[V][W] = 0;
}
}
return pGraph;
}
void insertEdge(PtrGraph pGraph, PtrEdge pEdge) {//将边插入图中(修改邻接矩阵)
pGraph->AdjMatrix[pEdge->V1][pEdge->V2] = pEdge->Weight;
//如果是无向图
//pGraph->AdjMatrix[pEdge->V2][pEdge->V1] = pEdge->Weight;
}
PtrGraph buildGraph(void) {//创建有边和节点的图
int vertexNum, edgeNum;
printf("请输入顶点数:");
scanf_s("%d", &vertexNum); //输入顶点数
PtrGraph pGraph = initGraphWithoutEdge(vertexNum);
printf("请输入边数:");
scanf_s("%d", &edgeNum); //输入边数
pGraph->Ne = edgeNum;
if (edgeNum != 0) {
Edge E;
for (int i = 1; i <= edgeNum; i++) {
printf("请输入第%d条边的端点(起点):",i);
scanf_s("%d", &E.V1);//输入边的信息
printf("请输入第%d条边的端点(终点):",i);
scanf_s("%d", &E.V2);//输入边的信息
printf("请输入第%d条边的权重:",i);
scanf_s("%d", &E.Weight);//输入边的信息
insertEdge(pGraph, &E);
}
}
return pGraph;
}
两种遍历方式:
广度优先搜索
void BFS(PtrGraph pGraph, Vertex V) {//广度优先搜索(利用队列实现),从V节点开始
PtrQueue pQ = initQueue(MAX_SIZE); //创建队列
/*用数组记录已经访问的顶点,1表示已访问,0表示未访问*/
int* visited = newZeroArray(pGraph->Nv);
visitNode(V); //访问V节点
visited[V] = 1;
enQueue(pQ, V); //入队
Vertex S;
while (!isEmpty(pQ)) {
S = deQueue(pQ);
for (Vertex W = 0; W < pGraph->Nv; W++) {
if (visited[W]==0 && isEdge(pGraph, S, W)) {
visitNode(W);
visited[W] = 1;
enQueue(pQ, W); //入队
}
}
}
}
深度优先搜索
void DFS(PtrGraph pGraph, Vertex V) {//深度优先搜索,从V节点开始
/*用数组记录已经访问的顶点,1表示已访问,0表示未访问*/
int* visited = newZeroArray(pGraph->Nv);
/*访问节点V,再遍历其他邻居的图(不访问已被访问过的节点)*/
DFStraversal(pGraph, V, visited);
}
void DFStraversal(PtrGraph pGraph, Vertex V, int* visited) {
/*访问节点V*/
visitNode(V);
visited[V] = 1;
/*已相同的方式遍历V的邻居*/
for (Vertex W = 0; W < pGraph->Nv; W++)
if (visited[W] == 0 && isEdge(pGraph, W, V))//W是V未被访问的邻居
DFStraversal(pGraph, W, visited);
}
void visitNode(Vertex V) {//访问V节点
printf("正在访问%d节点\n", V);
}
bool isEdge(PtrGraph pGraph, Vertex W, Vertex V) {//判断<W,V>是不是图中的边
//temp=0 说明不是是无权图的边,无权图的边为1
//temp=INFINITY说明不是是加权图的边,加权图的边 <INFINITY && >0(0说明W 和V是同一个点
int temp = pGraph->AdjMatrix[W][V];
return temp != 0 && temp != INFINITY;
}
int* newZeroArray(int length) {
int* visited = newArray(length);
for (int W = 0; W < length; W++)
visited[W] = 0;
return visited;
}
最短路径问题
无权图的单源最短路算法
void unweigtedShortestPath(PtrGraph pGraph, Source S, Vertex* path, int* distance) {//无权图的单源最短路算法
/*S是源点,path记录到顶点的要经过的顶点,distance记录到顶点的最短路径*/
/*类似于广度优先搜索的算法,无加权时,先到的就是最短的*/
distance[S] = 0;
PtrQueue pQ = initQueue(pGraph->Nv);
enQueue(pQ, S);
Vertex V;
while (!isEmpty(pQ)) {
V = deQueue(pQ);
for(Vertex W = 0;W<pGraph->Nv;W++)
if (distance[W] == -1 && isEdge(pGraph, V, W)) {//W是V的邻接点
distance[W] = distance[V] + 1;
path[W] = V;
enQueue(pQ, W);
}
}
}
加权图的单源最短路算法:Dijkstra算法
void Dijkstra(PtrGraph pGraph, Source S, Vertex* path, int* distance){//有源加权图,所有权都为正
int* collected = newZeroArray(pGraph->Nv);
distance[S] = 0;
Vertex V;
while (1) {
V = findMinDist(distance, pGraph->Nv, collected);//寻未被搜集中的最小值
if (V == -1)//这样的V不存在,所有顶点都被搜集了
break;
else {
collected[V] = 1;
for (Vertex W = 0; W < pGraph->Nv; W++)
if (isEdge(pGraph, V, W) && collected[W] == 0) //W是V的邻接点,并且W没有被搜集
if (distance[W] > distance[V] + pGraph->AdjMatrix[V][W]) {
distance[W] = distance[V] + pGraph->AdjMatrix[V][W];
path[W] = V;
}
}
}
}
int* newZeroArray(int length) {
int* visited = newArray(length);
for (int W = 0; W < length; W++)
visited[W] = 0;
return visited;
}
Vertex findMinDist(int* dist, int length, int* collected) {//寻未被搜集中的最小值
int min = INFINITY;
int minVertex;
for (int i = 0; i < length; i++)
if (collected[i] == 0) //表示未被搜集
if (dist[i] < min) {
min = dist[i];
minVertex = i;
}
//如果找不到这样的点,返回-1
if (min == INFINITY)
return -1;
else
return minVertex;
}
多源加权图最短路径:Floyd算法
void Floyd(PtrGraph pGraph, Vertex path[MAX_SIZE][MAX_SIZE], int distance[MAX_SIZE][MAX_SIZE]) {//多源加权图最短路径
for (Vertex V = 0; V < pGraph->Nv; V++)
for (Vertex W = 0; W < pGraph->Nv; W++)
distance[V][W] = pGraph->AdjMatrix[V][W];
for (Vertex M = 0; M < pGraph->Nv; M++)
for (Vertex V = 0; V < pGraph->Nv; V++)
for (Vertex W = 0; W < pGraph->Nv; W++)
if (distance[V][W] > distance[V][M] + distance[M][W]) {
distance[V][W] = distance[V][M] + distance[M][W];
path[V][W] = M;
}
}