前言:图的知识点整理,欢迎评论交流。
数据结构--图(C++)
图的基本概念
图的定义,图由顶点集V和边集E组成,记为G=(V,E),其中V={v1,v2,v3,……,v(n)},E={(u,v)|u∈V,v∈V}。
图(graph)、顶点(vertex)、边(dege)
1.无向图、有向图及混合图
若E为无向边(undirected edge),则图G为无向图(undigraph)
若E为有向边(directed edge),则图G为有向图(digraph)
若E同时包含无向边和有向边,则图G为混合图(mixed graph)
2.简单图,不含任何自环(self-loop)的图称作简单图(simple graph)
3.连通图和连通分量
若图G中任意两个顶点都是连通的,则称图G为连通图。
无向图中的极大连通子图称为连通分量。
4.强连通图和强连通分量
有向图中,若从顶点v到顶点w和从顶点w到顶点v之间都有路径,则称这两个顶点是强连通的。
若图中任意一对顶点都是强连通的,则称此图为强连通图。
有向图中的极大强连通子图称为有向图的强连通分量。
5.顶点的度、入度和出度
无向图中,与顶点v关联的边数,称作v的度数(degree),记作deg(v)。
有向图中,顶点的出边总数称为出度(out-degree),记作outdeg(v),入边总数称为入度(in-degree),记作indeg(v)。
6.路径、路径长度和回路
顶点v1到顶点v3之间的一条路径是指顶点序列v1,v2,v3。
路径上边的数目称为路径长度。
第一个顶点和最后一个顶点相同的路径称为回路或环。
7.简单路径、简单回路
在路径序列中,顶点不重复出现的路径称为简单路径。
除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路
8.带权网络
各边均带有权重的图,称为带权图(weighted graph)或带权网络(weighted network)。
图结构的存储
邻接矩阵
邻接矩阵的存储结构
用一个一维数组vertices[]存储顶点信息,用一个二维数组edge[]存储边的信息,变量vexnum存储顶点数,变量edgenum存储边数。
代码片
.
struct MGraph //邻接矩阵
{
VertexType Vertices[MaxVertexNum];
EdgeType Edge[MaxVertexNum][MaxVertexNum];
int vexnum;
int edgenum;
};
创建一个邻接矩阵图
代码片
.
#include<iostream>__msvc_all_public_headers.hpp
#include<queue>
using namespace std;
const int MaxVertexNum = 100;
const int MaxWeight = 1000;
const int inf = 100000;
typedef char VertexType;
typedef int EdgeType;
struct MGraph //邻接矩阵
{
VertexType Vertices[MaxVertexNum];
EdgeType Edge[MaxVertexNum][MaxVertexNum];
int vexnum;
int edgenum;
};
//初始化
void init(MGraph &G)
{
for (int i = 0;i < MaxVertexNum;i++)
G.Vertices[i] = 0;
for(int i=0;i<MaxVertexNum;i++)
for(int j=0;j<MaxVertexNum;j++)
G.Edge[i][j] = inf;
G.vexnum = 0;
G.edgenum = 0;
}
//得到顶点位置
int getVertexPos(MGraph &G, VertexType vertex)
{
for (int i = 0;i < MaxVertexNum;i++)
if (G.Vertices[i] == vertex) return i;
return false;
}
//插入顶点
bool insertVertex(MGraph &G, VertexType vertex)
{
if (G.vexnum == MaxVertexNum) return false; //顶点表已满,返回false
G.Vertices[G.vexnum++] = vertex;
return true;
}
//插入边
bool insertEdge(MGraph &G, VertexType vertex1, VertexType vertex2, EdgeType weight)
{
int v1 = getVertexPos(G, vertex1);
int v2 = getVertexPos(G, vertex2);
if (v1 > -1 && v1<MaxVertexNum && v2>-1 && v2 < MaxVertexNum)
{
G.Edge[v1][v2] =G.Edge[v2][v1]= weight; //无向图
G.edgenum++;
return true;
}
return false;
}
void test01()
{
char A = 'A', B = 'B', C = 'C', D = 'D', E = 'E', F = 'F';
MGraph g;
init(g);
insertVertex(g, A);
insertVertex(g, B);
insertVertex(g, C);
insertVertex(g, D);
insertVertex(g, E);
insertVertex(g, F);
insertEdge(g, A, B, 1);
insertEdge(g, A, C, 1);
insertEdge(g, A, D, 1);
insertEdge(g, A, F, 1);
insertEdge(g, B, E, 1);
insertEdge(g, B, F, 1);
insertEdge(g, C, D, 1);
}
int main()
{
test01();
return 0;
}
邻接表
邻接表的存储结构
代码片
.
const int MaxVertexNum = 100;
typedef char VertexType;
//边表结点
struct EdgeNode
{
int adjvex;
EdgeNode* next;
};
//顶点表结点
typedef struct VexNode
{
VertexType data;
EdgeNode* first;
}VexNode,AdjList[MaxVertexNum];
//邻接表
struct ALGraph
{
AdjList vertices;
int vexnum;
int edgenum;
};
创建一个邻接表
代码片
.
#include<iostream>__msvc_all_public_headers.hpp
#include<queue>accountssettingspaneinterop.h
using namespace std;
const int MaxVertexNum = 100;
typedef char VertexType;
//边表结点
struct EdgeNode
{
int adjvex;
EdgeNode* next;
};
//顶点表结点
typedef struct VexNode
{
VertexType data;
EdgeNode* first;
}VexNode,AdjList[MaxVertexNum];
//邻接表
struct ALGraph
{
AdjList vertices;
int vexnum;
int edgenum;
};
//初始化
void init(ALGraph& G)
{
for (int i = 0;i < MaxVertexNum;i++)
{
G.vertices[i].data = 0;
G.vertices[i].first = 0;
}
G.vexnum = 0;
G.edgenum = 0;
}
//顶点位置
int getVertexPos(ALGraph G, VertexType vertex)
{
for (int i = 0;i < G.vexnum;i++)
if (G.vertices[i].data ==vertex)
return i;
return -1;
}
//插入顶点
bool insertVertex(ALGraph& G, VertexType vertex)
{
if (G.vexnum == MaxVertexNum) return false;
G.vertices[G.vexnum++].data = vertex;
return true;
}
//插入边
void insertEdge(ALGraph& G, VertexType vertex1, VertexType vertex2)
{
int v1 = getVertexPos(G, vertex1);
int v2 = getVertexPos(G, vertex2);
EdgeNode* enode = new EdgeNode;
enode->adjvex =v2;
enode->next = NULL;
if (G.vertices[v1].first == NULL)
G.vertices[v1].first = enode;
else
{
EdgeNode* p;
p = G.vertices[v1].first;
while (p->next != NULL)
p = p->next;
p->next = enode;
}
EdgeNode* enode2 = new EdgeNode;
enode2->adjvex = v1;
enode2->next = NULL;
if (G.vertices[v2].first == NULL)
G.vertices[v2].first = enode2;
else
{
EdgeNode* p;
p = G.vertices[v2].first;
while (p->next != NULL)
p = p->next;
p->next = enode2;
}
G.edgenum++;
}
void test()
{
char A = 'A', B = 'B', C = 'C', D = 'D', E = 'E', F = 'F';
ALGraph g;
init(g);
insertVertex(g, A);
insertVertex(g, B);
insertVertex(g, C);
insertVertex(g, D);
insertVertex(g, E);
insertVertex(g, F);
insertEdge(g, A, B);
insertEdge(g, A, C);
insertEdge(g, A, D);
insertEdge(g, A, F);
insertEdge(g, B, E);
insertEdge(g, B, F);
insertEdge(g, C, D);
}
int main()
{
test();
return 0;
}
十字链表
十字链表是有向图的一种链式存储结构。
参考:https://blog.csdn.net/bible_reader/article/details/71214096
链接多重表
链接多重表是无向图的一种链式存储结构。
参考:https://blog.csdn.net/bible_reader/article/details/71250117
图的遍历
邻接矩阵的广度优先搜索
代码片
.
queue<int> Q; //广度优先遍历
bool visited[MaxVertexNum];
void BFS(MGraph G, int v)
{
cout << G.Vertices[v] << endl; //visit(v)
visited[v] = true;
Q.push(v);
while (!Q.empty())
{
Q.pop();
for (int w = FirstNeighbor(G, v);w >= 0;w = NextNeighbor(G, v, w))
if (!visited[w])
{
cout << G.Vertices[w] << endl;
visited[w] = true;
Q.push(w);
}
}
}
void BFStraverse(MGraph G)
{
for (int i = 0;i < G.vexnum;i++)
visited[i] = false;
for (int i = 0;i < G.vexnum;i++)
{
if (!visited[i])
BFS(G, i);
}
}
遍历结果
邻接矩阵的深度优先搜索
代码片
.
void DFS(MGraph G, int v)
{
cout << G.Vertices[v] << endl;
visited[v] = true;
for (int w = FirstNeighbor(G, v);w >= 0;w = NextNeighbor(G, v, w))
if (!visited[w])
DFS(G, w);
}
void DFStraverse(MGraph G)
{
for (int i = 0;i < G.vexnum;i++)
visited[i] = false;
for (int i = 0;i < G.vexnum;i++)
if (!visited[i])
DFS(G, i);
}
遍历结果
邻接表的广度优先搜索
代码片
.
bool visited[MaxVertexNum]; //标记数组
//广度优先搜索
queue<int> Q;
void BFS(ALGraph G, int v)
{
cout << G.vertices[v].data << endl;
visited[v] = true;
Q.push(v);
while (!Q.empty())
{
Q.pop();
for (EdgeNode* p = G.vertices[v].first;p > 0;p = p->next)
if (!visited[p->adjvex])
{
cout << G.vertices[p->adjvex].data << endl;
visited[p->adjvex] = true;
Q.push(p->adjvex);
}
}
}
void BFStraverse(ALGraph G)
{
for (int i = 0;i < G.vexnum;i++)
visited[i] = 0;
for (int i = 0;i < G.vexnum;i++)
if (!visited[i])
BFS(G, i);
}
遍历结果
邻接表的深度优先搜索
代码片
.
bool visited[MaxVertexNum]; //标记数组
//深度优先搜索
void DFS(ALGraph G, int v)
{
cout << G.vertices[v].data << endl;
visited[v] = true;
for (EdgeNode* p = G.vertices[v].first;p > 0;p = p->next)
if (!visited[p->adjvex])
DFS(G, p->adjvex);
}
void DFStraverse(ALGraph 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);
}
遍历结果
图的应用
最小生成树
生成树,一个连通图的生成树是图的极小连通子图,它包含图中的所有顶点,并且只含尽可能少的边。这意味着对于生成树来说,若砍去它的一条边,则会使生成树变成非连通图;若给它增加一条边,则会形成图中的一条回路。
最小生成树,带权连通无向图G=(V,E)中,所有生成树中权值最小的那棵生成树,称为G的最小生成树(Minimum Spanning Tree,MST)。
性质
(1).最小生成树不是唯一的,即最小生成树的树形不唯一。
(2).最小生成树的边的权值之和总是唯一的,而且是最小的。
(3).最小生成树的边数为顶点数减一。
参考:最小生成树的动画演示https://www.bilibili.com/video/BV1Eb41177d1/
1.Prim算法
代码片
.
struct MST //最小生成树
{
VertexType Vertices[MaxVertexNum]; //顶点集
EdgeType Edge[MaxVertexNum][MaxVertexNum];//边集
int vexnum;//顶点数
int edgenum;//边数
EdgeType lowcost[MaxVertexNum];//权值表
EdgeType weight;//树的权值
};
void initMST(MST &T) //初始化
{
for (int i = 0;i < MaxVertexNum;i++)
{
T.Vertices[i] = 0;
T.lowcost[i] =0;
}
for (int i = 0;i < MaxVertexNum;i++)
for (int j = 0;j < MaxVertexNum;j++)
T.Edge[i][j] = 0;
T.edgenum = 0;
T.vexnum = 0;
T.weight = 0;
}
int Min(int a[], int n)//数组中最小值的下标
{
int min = inf;
int v=0;
for (int i = 0;i < n;i++)
if (a[i] != 0 && a[i] < min)
{
min = a[i];
v = i;
}
return v;
}
void Prim(MGraph G, MST &T)
{
int v0= 0; //从顶点v0开始
int parent[MaxVertexNum];//标记数组,记录边尾节点的边头节点
T.Vertices[v0] = G.Vertices[v0];//将第一个顶点加入T中
T.vexnum++; //T的顶点数加一
for (int i = 1;i < G.vexnum;i++)
{
T.lowcost[i] = G.Edge[v0][i]; //边(v0,其他顶点)的权值表
parent[i] = v0;
}
while (T.vexnum != G.vexnum)
{
int Vtail = Min(T.lowcost, G.vexnum);//边(Vhead,其他顶点)权值最小的边尾节点Vtail
int Vhead= parent[Vtail]; //边(Vhead,Vtail),边尾Vtail,边头Vhead
T.Edge[Vhead][Vtail]=T.Edge[Vtail][Vhead] = G.Edge[Vhead][Vtail];//边归入树T中
T.Vertices[Vtail] = G.Vertices[Vtail];//顶点归入树T中
T.weight += T.Edge[Vhead][Vtail];//权值和
T.vexnum++;//顶点加一
T.edgenum++;//边加一
for (int i = 0;i < G.vexnum;i++)//更新权值表
{
if (G.Edge[Vtail][i] > 0 && G.Edge[Vtail][i] < T.lowcost[i])
{
T.lowcost[i] = G.Edge[Vtail][i];//更新了(Vtail,i)的权值
parent[i] = Vtail;//边尾Vtail的边头节点i
}
}
T.lowcost[Vtail] = 0;//已记录的权值归零
}
}
2.Kruskal算法
代码片
.
//边
struct Edge
{
int head, tail, weight;//边的头节点、尾节点和权值
};
//边集中插入边
void insEdge(MGraph G, Edge *edge)
{
int n = 0;
for (int i = 0;i < G.edgenum;i++)
for (int j = i+1;j < G.edgenum;j++)
if (G.Edge[i][j] !=inf)
{
edge[n].head = getVertexPos(G,G.Vertices[i]);
edge[n].tail = getVertexPos(G, G.Vertices[j]);
edge[n].weight = G.Edge[i][j];
n++;
}
}
//并查集
int Fa[MaxVertexNum];
void initFa(int *a,int n)
{
for (int i = 0;i < n;i++)
a[i] = i;
}
bool cmp(Edge& a, Edge& b)
{
return a.weight <= b.weight;
}
int findFa(int x)
{
if (Fa[x] != x) {
Fa[x] = findFa(Fa[x]); //找到并赋值,路径压缩
}
return Fa[x];
}
void Kruskal(MGraph G, MST& T)
{
for (int i = 0;i < G.vexnum;i++)//顶点赋值
T.Vertices[i] = G.Vertices[i];
T.vexnum = G.vexnum;//顶点数赋值
Edge edge[MaxVertexNum];//边集
insEdge(G, edge);//插入边
sort(edge, edge + G.edgenum, cmp);//边排序
initFa(Fa, G.edgenum);
for (int i = 0;i < G.edgenum;i++)//向T中依次添加边
if (findFa(edge[i].head) != findFa(edge[i].tail))
{
int head = edge[i].head;
int tail = edge[i].tail;
T.Edge[head][tail] = T.Edge[tail][head] = edge[i].weight;
T.edgenum++;
T.weight += edge[i].weight;
Fa[findFa(edge[i].tail)] = findFa(edge[i].head);
}
}
完整代码
#include<iostream>__msvc_all_public_headers.hpp
#include<algorithm>__msvc_all_public_headers.hpp
using namespace std;
const int MaxVertexNum =100;
const int MaxWeight = 1000;
const int inf = 100000;
typedef char VertexType ;
typedef int EdgeType ;
struct MGraph //邻接矩阵
{
VertexType Vertices[MaxVertexNum];
EdgeType Edge[MaxVertexNum][MaxVertexNum];
int vexnum;
int edgenum;
};
//初始化
void init(MGraph& G)
{
for (int i = 0;i < MaxVertexNum;i++)
G.Vertices[i] = 0;
for (int i = 0;i < MaxVertexNum;i++)
for (int j = 0;j < MaxVertexNum;j++)
G.Edge[i][j] = inf;
G.vexnum = 0;
G.edgenum = 0;
}
//得到顶点位置
int getVertexPos(MGraph& G, VertexType vertex)
{
for (int i = 0;i < MaxVertexNum;i++)
if (G.Vertices[i] == vertex) return i;
return false;
}
//插入顶点
bool insertVertex(MGraph& G, VertexType vertex)
{
if (G.vexnum == MaxVertexNum) return false; //顶点表已满,返回false
G.Vertices[G.vexnum++] = vertex;
return true;
}
//插入边
bool insertEdge(MGraph& G, VertexType vertex1, VertexType vertex2, EdgeType weight)
{
int v1 = getVertexPos(G, vertex1);
int v2 = getVertexPos(G, vertex2);
if (v1 > -1 && v1<MaxVertexNum && v2>-1 && v2 < MaxVertexNum)
{
G.Edge[v1][v2] = G.Edge[v2][v1] = weight; //无向图
G.edgenum++;
return true;
}
return false;
}
struct MST //最小生成树
{
VertexType Vertices[MaxVertexNum]; //顶点集
EdgeType Edge[MaxVertexNum][MaxVertexNum];//边集
int vexnum;//顶点数
int edgenum;//边数
EdgeType lowcost[MaxVertexNum];//权值表
EdgeType weight;//树的权值
};
void initMST(MST &T) //初始化
{
for (int i = 0;i < MaxVertexNum;i++)
{
T.Vertices[i] = 0;
T.lowcost[i] =0;
}
for (int i = 0;i < MaxVertexNum;i++)
for (int j = 0;j < MaxVertexNum;j++)
T.Edge[i][j] = 0;
T.edgenum = 0;
T.vexnum = 0;
T.weight = 0;
}
int Min(int a[], int n)//数组中最小值的下标
{
int min = inf;
int v=0;
for (int i = 0;i < n;i++)
if (a[i] != 0 && a[i] < min)
{
min = a[i];
v = i;
}
return v;
}
void Prim(MGraph G, MST &T)
{
int v0= 0; //从顶点v0开始
int parent[MaxVertexNum];//标记数组,记录边尾节点的边头节点
T.Vertices[v0] = G.Vertices[v0];//将第一个顶点加入T中
T.vexnum++; //T的顶点数加一
for (int i = 1;i < G.vexnum;i++)
{
T.lowcost[i] = G.Edge[v0][i]; //边(v0,其他顶点)的权值表
parent[i] = v0;
}
while (T.vexnum != G.vexnum)
{
int Vtail = Min(T.lowcost, G.vexnum);//边(Vhead,其他顶点)权值最小的边尾节点Vtail
int Vhead= parent[Vtail]; //边(Vhead,Vtail),边尾Vtail,边头Vhead
T.Edge[Vhead][Vtail]=T.Edge[Vtail][Vhead] = G.Edge[Vhead][Vtail];//边归入树T中
T.Vertices[Vtail] = G.Vertices[Vtail];//顶点归入树T中
T.weight += T.Edge[Vhead][Vtail];//权值和
T.vexnum++;//顶点加一
T.edgenum++;//边加一
for (int i = 0;i < G.vexnum;i++)//更新权值表
{
if (G.Edge[Vtail][i] > 0 && G.Edge[Vtail][i] < T.lowcost[i])
{
T.lowcost[i] = G.Edge[Vtail][i];//更新了(Vtail,i)的权值
parent[i] = Vtail;//边尾Vtail的边头节点i
}
}
T.lowcost[Vtail] = 0;//已记录的权值归零
}
}
//边
struct Edge
{
int head, tail, weight;//边的头节点、尾节点和权值
};
//边集中插入边
void insEdge(MGraph G, Edge *edge)
{
int n = 0;
for (int i = 0;i < G.edgenum;i++)
for (int j = i+1;j < G.edgenum;j++)
if (G.Edge[i][j] !=inf)
{
edge[n].head = getVertexPos(G,G.Vertices[i]);
edge[n].tail = getVertexPos(G, G.Vertices[j]);
edge[n].weight = G.Edge[i][j];
n++;
}
}
//并查集
int Fa[MaxVertexNum];
void initFa(int *a,int n)
{
for (int i = 0;i < n;i++)
a[i] = i;
}
bool cmp(Edge& a, Edge& b)
{
return a.weight <= b.weight;
}
int findFa(int x)
{
if (Fa[x] != x) {
Fa[x] = findFa(Fa[x]); //找到并赋值,路径压缩
}
return Fa[x];
}
void Kruskal(MGraph G, MST& T)
{
for (int i = 0;i < G.vexnum;i++)//顶点赋值
T.Vertices[i] = G.Vertices[i];
T.vexnum = G.vexnum;//顶点数赋值
Edge edge[MaxVertexNum];//边集
insEdge(G, edge);//插入边
sort(edge, edge + G.edgenum, cmp);//边排序
initFa(Fa, G.edgenum);
for (int i = 0;i < G.edgenum;i++)//向T中依次添加边
if (findFa(edge[i].head) != findFa(edge[i].tail))
{
int head = edge[i].head;
int tail = edge[i].tail;
T.Edge[head][tail] = T.Edge[tail][head] = edge[i].weight;
T.edgenum++;
T.weight += edge[i].weight;
Fa[findFa(edge[i].tail)] = findFa(edge[i].head);
}
}
void test01()
{
char A = 'A', B = 'B', C = 'C', D = 'D', E = 'E', F = 'F';
MGraph g;
init(g);
insertVertex(g, A);
insertVertex(g, B);
insertVertex(g, C);
insertVertex(g, D);
insertVertex(g, E);
insertVertex(g, F);
insertEdge(g, A, B, 4);
insertEdge(g, A, C, 1);
insertEdge(g, A, D, 6);
insertEdge(g, A, F, 3);
insertEdge(g, B, E, 9);
insertEdge(g, B, F, 2);
insertEdge(g, C, D, 5);
MST t;
initMST(t);//初始化
Prim(g, t);
cout << "Prim算法:" << endl;
for (int i = 0;i < t.vexnum;i++)//打印边及权值
for (int j = i + 1;j < t.vexnum;j++)
if (t.Edge[i][j] != 0)
cout << t.Vertices[i] << t.Vertices[j] << t.Edge[i][j] << endl;
cout <<"权值和 "<<t.weight << endl;
initMST(t);
Kruskal(g, t);
cout << "Kruskal算法:" << endl;
for (int i = 0;i < t.vexnum;i++)//打印边及权值
for (int j = i + 1;j < t.vexnum;j++)
if (t.Edge[i][j] != 0)
cout << t.Vertices[i] << t.Vertices[j] << t.Edge[i][j] << endl;
cout << "权值和 " << t.weight << endl;
}
int main()
{
test01();
return 0;
}
运行结果
最短路径
最短路径,图是带权图时,把从一个顶点v0到图中其余任意一个顶点vi的一条路径所经过边上的权值之和,定义为该路径的带权路径长度,把带权路径长度最短的那条路径称为最短路径。
1.Dijkstra算法
代码片
//最短路径
struct DijkstraPath
{
VertexType Vertices[MaxVertexNum];//顶点集
int vexnum;//顶点数
int visited[MaxVertexNum];//标记
int dist[MaxVertexNum];//距离
int path[MaxVertexNum];//路径
};
void initSP(DijkstraPath& P)//初始化
{
for (int i = 0;i < MaxVertexNum;i++)
{
P.visited[i] = 0;
P.dist[i] = 0;
P.path[i] = 0;
}
}
int Min(DijkstraPath &P,int n)//P.dist[]最小值的下标
{
int min = inf;
int v = 0;
for (int i = 0;i < n;i++)
if (!P.visited[i]&& P.dist[i] < min)
{
min = P.dist[i];
v = i;
}
return v;
}
void Dijkstra(MGraph G,DijkstraPath &P)
{
int v0 = 0;
for (int i = 0;i < G.vexnum;i++)//赋值
{
P.Vertices[i] = G.Vertices[i];
P.dist[i] = G.Edge[v0][i]; //顶点到其他顶点的权值
}
P.vexnum = G.vexnum;
P.visited[v0] = 1;
for (int i = 0;i < P.vexnum;i++)
{
int next = Min(P,G.vexnum);//边(Vhead,其他顶点)权值最小的边尾节点next
P.visited[next] = 1;
for (int i = 0;i < P.vexnum;i++)
{
if ((G.Edge[next][i] + P.dist[next]) < P.dist[i] && !P.visited[i])
{
P.dist[i] = G.Edge[next][i] + P.dist[next];
P.path[i] = next;//记录路径
}
}
}
}
//打印输出
void printP(DijkstraPath P, char x)//从顶点到x的最短路径
{
stack<int> s;
int tail=0,in=0;
P.path[0] =inf;
tail=in = getVertexPos(P.Vertices, x);//x的顶点序号
while(in!=inf)//入栈
{
s.push(in);
in = P.path[in];
}
cout << "Dijkstra算法"<<endl;
cout << P.Vertices[0] << "到" << P.Vertices[tail] << "最短路径: ";
while(!s.empty())//出栈
{
int out=s.top() ;
cout << P.Vertices[out] << " ";
s.pop();
}
cout << "权值:" << P.dist[tail] << endl;
}
2.Floyd算法
代码片
struct FloydPath
{
VertexType Vertices[MaxVertexNum];//顶点集
int vexnum;//顶点数
int dist[MaxVertexNum][MaxVertexNum];
int path[MaxVertexNum][MaxVertexNum];
};
void initFP(MGraph G, FloydPath &F)
{
for (int row = 0;row < G.vexnum;row++)
for (int col = 0;col < G.vexnum;col++)
{
F.dist[row][col] = G.Edge[row][col];
F.path[row][col] = col;
}
for (int i = 0;i < G.vexnum;i++)//赋值
F.Vertices[i] = G.Vertices[i];
F.vexnum = G.vexnum;
}
void Floyd(MGraph G, FloydPath& F)
{
initFP(G, F);
for (int mid = 0;mid < G.vexnum;mid++)
for (int row = 0;row < G.vexnum;row++)
for (int col = 0;col < G.vexnum;col++)
if (F.dist[row][mid] + F.dist[mid][col] < F.dist[row][col])
{
F.dist[row][col] = F.dist[row][mid] + F.dist[mid][col];
F.path[row][col] = F.path[row][mid];
}
}
void printF(FloydPath& F, char x1, char x2)
{
stack<int> s;
int v1 = getVertexPos(F.Vertices, x1);
int v2 = getVertexPos(F.Vertices, x2);
cout << "Floyd算法" << endl;
cout << F.Vertices[v1] << "到" << F.Vertices[v2] << "最短路径: ";
cout << F.Vertices[v1]<<" ";
int temp = 0, first = v1;
while (v1 != v2)
{
temp = F.path[v1][v2];
cout << F.Vertices[temp] << " ";
v1 = temp;
}
cout << "权值 " << F.dist[first][v2];
}
完整代码
#include<iostream>__msvc_all_public_headers.hpp
#include<algorithm>__msvc_all_public_headers.hpp
#include<stack>__msvc_all_public_headers.hpp
using namespace std;
const int MaxVertexNum = 100;
const int MaxWeight = 1000;
const int inf = 100000;
typedef char VertexType;
typedef int EdgeType;
struct MGraph //邻接矩阵
{
VertexType Vertices[MaxVertexNum];
EdgeType Edge[MaxVertexNum][MaxVertexNum];
int vexnum;
int edgenum;
};
//初始化
void init(MGraph& G)
{
for (int i = 0;i < MaxVertexNum;i++)
G.Vertices[i] = 0;
for (int i = 0;i < MaxVertexNum;i++)
for (int j = 0;j < MaxVertexNum;j++)
G.Edge[i][j] = inf;
G.vexnum = 0;
G.edgenum = 0;
}
//得到顶点位置
int getVertexPos(VertexType *a, VertexType vertex)
{
for (int i = 0;i < MaxVertexNum;i++)
if (a[i] == vertex) return i;
return false;
}
//插入顶点
bool insertVertex(MGraph& G, VertexType vertex)
{
if (G.vexnum == MaxVertexNum) return false; //顶点表已满,返回false
G.Vertices[G.vexnum++] = vertex;
return true;
}
//插入边
bool insertEdge(MGraph& G, VertexType vertex1, VertexType vertex2, EdgeType weight)
{
int v1 = getVertexPos(G.Vertices, vertex1);
int v2 = getVertexPos(G.Vertices, vertex2);
if (v1 > -1 && v1<MaxVertexNum && v2>-1 && v2 < MaxVertexNum)
{
G.Edge[v1][v2] = G.Edge[v2][v1] = weight; //无向图
G.edgenum++;
return true;
}
return false;
}
//最短路径
struct DijkstraPath
{
VertexType Vertices[MaxVertexNum];//顶点集
int vexnum;//顶点数
int visited[MaxVertexNum];//标记
int dist[MaxVertexNum];//距离
int path[MaxVertexNum];//路径
};
void initSP(DijkstraPath& P)//初始化
{
for (int i = 0;i < MaxVertexNum;i++)
{
P.visited[i] = 0;
P.dist[i] = 0;
P.path[i] = 0;
}
}
int Min(DijkstraPath &P,int n)//P.dist[]最小值的下标
{
int min = inf;
int v = 0;
for (int i = 0;i < n;i++)
if (!P.visited[i]&& P.dist[i] < min)
{
min = P.dist[i];
v = i;
}
return v;
}
void Dijkstra(MGraph G,DijkstraPath &P)
{
initSP(P);
int v0 = 0;
for (int i = 0;i < G.vexnum;i++)//赋值
{
P.Vertices[i] = G.Vertices[i];
P.dist[i] = G.Edge[v0][i]; //顶点到其他顶点的权值
}
P.vexnum = G.vexnum;
P.visited[v0] = 1;
for (int i = 0;i < P.vexnum;i++)
{
int next = Min(P,G.vexnum);//边(Vhead,其他顶点)权值最小的边尾节点next
P.visited[next] = 1;
for (int i = 0;i < P.vexnum;i++)
{
if ((G.Edge[next][i] + P.dist[next]) < P.dist[i] && !P.visited[i])
{
P.dist[i] = G.Edge[next][i] + P.dist[next];
P.path[i] = next;//记录路径
}
}
}
}
//打印输出
void printP(DijkstraPath P, char x)//从顶点到x的最短路径
{
stack<int> s;
int tail=0,in=0;
P.path[0] =inf;
tail=in = getVertexPos(P.Vertices, x);//x的顶点序号
while(in!=inf)//入栈
{
s.push(in);
in = P.path[in];
}
cout << "Dijkstra算法"<<endl;
cout << P.Vertices[0] << "到" << P.Vertices[tail] << "最短路径: ";
while(!s.empty())//出栈
{
int out=s.top() ;
cout << P.Vertices[out] << " ";
s.pop();
}
cout << "权值:" << P.dist[tail] << endl;
}
struct FloydPath
{
VertexType Vertices[MaxVertexNum];//顶点集
int vexnum;//顶点数
int dist[MaxVertexNum][MaxVertexNum];
int path[MaxVertexNum][MaxVertexNum];
};
void initFP(MGraph G, FloydPath &F)
{
for (int row = 0;row < G.vexnum;row++)
for (int col = 0;col < G.vexnum;col++)
{
F.dist[row][col] = G.Edge[row][col];
F.path[row][col] = col;
}
for (int i = 0;i < G.vexnum;i++)//赋值
F.Vertices[i] = G.Vertices[i];
F.vexnum = G.vexnum;
}
void Floyd(MGraph G, FloydPath& F)
{
initFP(G, F);
for (int mid = 0;mid < G.vexnum;mid++)
for (int row = 0;row < G.vexnum;row++)
for (int col = 0;col < G.vexnum;col++)
if (F.dist[row][mid] + F.dist[mid][col] < F.dist[row][col])
{
F.dist[row][col] = F.dist[row][mid] + F.dist[mid][col];
F.path[row][col] = F.path[row][mid];
}
}
void printF(FloydPath& F, char x1, char x2)
{
stack<int> s;
int v1 = getVertexPos(F.Vertices, x1);
int v2 = getVertexPos(F.Vertices, x2);
cout << "Floyd算法" << endl;
cout << F.Vertices[v1] << "到" << F.Vertices[v2] << "最短路径: ";
cout << F.Vertices[v1]<<" ";
int temp = 0, first = v1;
while (v1 != v2)
{
temp = F.path[v1][v2];
cout << F.Vertices[temp] << " ";
v1 = temp;
}
cout << "权值 " << F.dist[first][v2];
}
void test01()
{
char A = 'A', B = 'B', C = 'C', D = 'D', E = 'E', F = 'F';
MGraph g;
init(g);
insertVertex(g, A);
insertVertex(g, B);
insertVertex(g, C);
insertVertex(g, D);
insertVertex(g, E);
insertVertex(g, F);
insertEdge(g, A, B, 5);
insertEdge(g, A, D, 3);
insertEdge(g, B, D, 1);
insertEdge(g, B, C, 1);
insertEdge(g, C, D, 4);
insertEdge(g, D, E, 7);
insertEdge(g, C, E, 3);
insertEdge(g, B, F, 8);
insertEdge(g, C, F, 12);
insertEdge(g, E, F, 3);
DijkstraPath p;
Dijkstra(g, p);
printP(p, F);//打印从顶点到F的最短路径
FloydPath f;
Floyd(g, f);
printF(f, C, F);//打印最短路径
}
int main()
{
test01();
return 0;
}
结果
拓扑排序
有向无环图,若一个有向图中不存在环,则称为有向无环图,简称DAG图。
AOV网,若用DAG表示一个工程,其顶点表示活动,用有向边<vi,vj>表示活动vi必须先于活动vj进行的这样一种关系,则将这种有向图称为顶点表示活动的网络,记作AOV网。
性质
1.每个顶点出现且只出现一次。
2.若顶点A在序列中排在顶点B的前面,则在图中不存在从顶点B到顶点A的路径。
代码
//拓扑排序
void ALGraph::TopologicalSort()
{
stack<int> S;
for (int i = 0;i < vexnum;i++)
if (vertices[i].indegree == 0)
S.push(i);//入度为零的顶点入栈
int count = 0,t=0;
VertexType printT[MaxVertexNum];
while (!S.empty())
{
t = S.top();
printT[count++] = vertices[t].data;//保存输出顶点
S.pop();
//list.push(t);//关键路径要用
for (EdgeNode* p = vertices[t].firstarc;p;p = p->nextarc)//顶点的邻接顶点
{
int v = p->adjvex;
if (!(--vertices[v].indegree))//邻接顶点的入度减一
S.push(v);//入度为零的顶点入栈
//if ((vertices[t].ve + p->weight) > vertices[v].ve) {
//vertices[v].ve = vertices[t].ve + p->weight;
}
}
}
cout << "拓扑排序:" << endl;//打印输出
for (int i = 0;i < vexnum;i++)
cout << printT[i] << " ";
}
关键路径
AOE网,在带权有向图中,以顶点表示事件,以有向边表示活动,以边上的权值表示完成该活动的开销(如完成活动所需要的时间),则称这种有向图为用边表示活动的网络,简称为AOE网。
性质
1.只有在某顶点所代表的事件发生后,从该顶点出发的各有向边所代表的活动才开始。
2.只有在进入某一顶点的各有向边所代表的活动都已结束时,该顶点所代表的事件才能发生。
代码
#include<iostream>__msvc_all_public_headers.hpp
#include<stack>
using namespace std;
const int MaxVertexNum = 100;
typedef char VertexType;
//边表结点
struct EdgeNode
{
int adjvex;//邻接点域
EdgeNode* nextarc;//链域
int weight;//权值
};
//顶点表结点
typedef struct VexNode
{
VertexType data;//数据域
EdgeNode* firstarc;//链域
int indegree;//入度
int outdegree;//出度
int ve; //记录每个顶点的最早发生时间
int vl; //记录每个顶点最迟发生时间
}VexNode,AdjList[MaxVertexNum];//顶点数组
//邻接表
struct ALGraph
{
AdjList vertices;//顶点集
int vexnum;
int edgenum;
void TopologicalSort();
void CriticalPath();
stack<int> list;
};
//初始化
void init(ALGraph& G)
{
for (int i = 0;i < MaxVertexNum;i++)
{
G.vertices[i].data = 0;
G.vertices[i].firstarc = 0;
G.vertices[i].indegree = 0;
G.vertices[i].outdegree = 0;
G.vertices[i].ve = 0;
G.vertices[i].vl = 0;
}
G.vexnum = 0;
G.edgenum = 0;
}
//顶点位置
int getVertexPos(ALGraph& G, VertexType vertex)
{
for (int i = 0;i < G.vexnum;i++)
if (G.vertices[i].data ==vertex)
return i;
return -1;
}
//插入顶点
bool insertVertex(ALGraph& G, VertexType vertex)
{
if (G.vexnum == MaxVertexNum) return false;
G.vertices[G.vexnum++].data = vertex;
return true;
}
//插入边
void insertEdge(ALGraph& G, VertexType vertex1, VertexType vertex2,int w)
{
int v1 = getVertexPos(G, vertex1);//得到顶点序号
int v2 = getVertexPos(G, vertex2);
EdgeNode* enode = new EdgeNode;
enode->adjvex =v2;
enode->nextarc = NULL;
enode->weight = w;
if (G.vertices[v1].firstarc == NULL)
{
G.vertices[v1].firstarc = enode;//链接下一个顶点
G.vertices[v1].outdegree++;//出度加一
G.vertices[v2].indegree++;//入度加一
}
else
{
EdgeNode* p;
p = G.vertices[v1].firstarc;
while (p->nextarc != NULL)
p = p->nextarc;
p->nextarc = enode;
G.vertices[v1].outdegree++;
G.vertices[v2].indegree++;
}
G.edgenum++;
}
//拓扑排序
void ALGraph::TopologicalSort()
{
stack<int> S;
for (int i = 0;i < vexnum;i++)
if (vertices[i].indegree == 0)
S.push(i);//入度为零的顶点入栈
int count = 0,t=0;
//VertexType printT[MaxVertexNum];
while (!S.empty())
{
t = S.top();
//printT[count++] = vertices[t].data;//保存输出顶点
S.pop();
list.push(t);//关键路径要用
for (EdgeNode* p = vertices[t].firstarc;p;p = p->nextarc)//顶点的邻接顶点
{
int v = p->adjvex;
if (!(--vertices[v].indegree))//邻接顶点的入度减一
S.push(v);//入度为零的顶点入栈
if ((vertices[t].ve + p->weight) > vertices[v].ve) {
vertices[v].ve = vertices[t].ve + p->weight;
}
}
}
//cout << "拓扑排序:" << endl;//打印输出
//for (int i = 0;i < vexnum;i++)
// cout << printT[i] << " ";
}
void ALGraph::CriticalPath()
{
TopologicalSort();
for (int k = 0; k < this->vexnum; k++) {
vertices[k].vl = vertices[this->vexnum - 1].ve;
}
while (!list.empty())
{
int t = list.top();
list.pop();
for (EdgeNode* p = vertices[t].firstarc;p;p = p->nextarc)//顶点的邻接顶点
{
int v = p->adjvex;
if (vertices[t].vl > (vertices[v].vl - p->weight)) {
vertices[t].vl = vertices[v].vl - p->weight;
}
}
}
int ee;
int el;
for (int k = 0; k < this->vexnum; k++)
{
for (EdgeNode* p = vertices[k].firstarc;p;p = p->nextarc)//顶点的邻接顶点
{
int ee;
int el;
for (int k = 0; k < this->vexnum; k++)
{
for (EdgeNode* p = vertices[k].firstarc;p;p = p->nextarc)//顶点的邻接顶点
{
int v = p->adjvex;
ee = vertices[k].ve;
el = vertices[v].vl - p->weight;
if (ee == el)
{
cout << this->vertices[k].data<< "--"<< this->vertices[v].data<< "="<< p->weight<< endl;
}
}
}
}
void test()
{
char A = 'v0', B = 'B', C = 'C', D = 'D', E = 'E';
ALGraph g;
init(g);
insertVertex(g, A);
insertVertex(g, B);
insertVertex(g, C);
insertVertex(g, D);
insertVertex(g, E);
insertEdge(g, A, B,1);
insertEdge(g, A, D,4);
insertEdge(g, B, D,3);
insertEdge(g, B, C,2);
insertEdge(g, D, C,5);
insertEdge(g, D, E,6);
insertEdge(g, C, E,9);
g.TopologicalSort();
}
void test01()
{
char A = 'A', B = 'B', C = 'C', D = 'D', E = 'E',F='F',G='G',H='H',I='I';
ALGraph g;
init(g);
insertVertex(g, A);
insertVertex(g, B);
insertVertex(g, C);
insertVertex(g, D);
insertVertex(g, E);
insertVertex(g, F);
insertVertex(g, G);
insertVertex(g, H);
insertVertex(g, I);
insertEdge(g, A, B, 6);
insertEdge(g, A, C, 4);
insertEdge(g, A, D, 5);
insertEdge(g, B, E, 1);
insertEdge(g, C, E, 1);
insertEdge(g, D, F, 2);
insertEdge(g, E, G, 9);
insertEdge(g, E, H, 7);
insertEdge(g, F, H, 4);
insertEdge(g, G, I, 2);
insertEdge(g, H, I, 4);
g.CriticalPath();
}
int main()
{
//test();
test01();
return 0;
}