选择合适的结构存储图,编写程序,
实现图的两种遍历方法:深度优先搜索 和 广度优先搜索,并输出对应的生成树。
//结构体_弧边
typedef struct Arc {
Info name; //弧边名称
int weight; //弧边权重
}Arc;
//结构体_节点
typedef struct Node {
Info name; //节点名称
int index; //节点序号
bool visited; //访问标记
}Node;
//结构体_图
typedef struct Graph {
Arc AdjMatrix[MAX_NUM][MAX_NUM]; //邻接矩阵
Node Nodes[MAX_NUM]; //节点数组
int Arc_Num; //弧边数量
int Node_Num; //节点数量
}Graph;
本实验采用 邻接矩阵 结构进行存储:使用 二维结构体数组 ,矩阵的行列序号表示连接边的起点和终点的序号。
//深度优先搜索 时间复杂度: O(Arc_num+Node_num)
void DFSTraverse(Graph &G, int n, bool(*Visit)(Node &N))
{
//参数n表示由第n号节点开始遍历
Visit(G.Nodes[n]);
for (int i = 0; i < G.Node_Num; i++) {
//若存在通路且另一节点未访问过
if (G.AdjMatrix[n][i].weight > 0 && !G.Nodes[i].visited) {
//打印弧边信息
cout << G.AdjMatrix[n][i].name << " ";
//递归访问
DFSTraverse(G, i, PrintNodeInfo);
}
}
}
深度优先搜索采用 递归 算法实现,由图的某一个结点开始 逐个分支地尽可能延伸 ,其 Visit 访问结点函数如下:
//打印节点信息
bool PrintNodeInfo(Node &n)
{
n.visited = true;
cout << n.name << " ";
return true;
}
访问结点函数将结点信息打出后,会将其 visited 标记置为 true,表示已访问,防止后续重复访问同一个结点。
深度优先搜索也可采用 堆栈 结构实现。
//广度优先搜索 时间复杂度: O(Arc_num+Node_num)
void BFSTraverse(Graph &G, int n, bool(*Visit)(Node &N))
{
Node u;
queue<Node> q; //辅助队列
Visit(G.Nodes[n]);
q.push(G.Nodes[n]); //入队
while (!q.empty()) {
//返回队首元素,并出队
u = q.front();
q.pop();
for (int i = 0; i < G.Node_Num; i++) {
//u.index 元素的序号
if (G.AdjMatrix[u.index][i].weight > 0 && !G.Nodes[i].visited) {
//打印弧边信息
cout << G.AdjMatrix[u.index][i].name << " ";
//访问
Visit(G.Nodes[i]);
//入队
q.push(G.Nodes[i]);
}
}
}
}
广度优先搜索采用 队列 结构实现,由某一个结点开始,一层一层地向周围点延伸 。
其 Visit 访问结点函数与深度优先搜索相同。
#define MAX_NUM 20 //最大节点数量
#define Info string //弧边与节点的信息
#include
#include
#include
using namespace std;
//结构体_弧边
typedef struct Arc {
Info name; //弧边名称
int weight; //弧边权重
}Arc;
//结构体_节点
typedef struct Node {
Info name; //节点名称
int index; //节点序号
bool visited; //访问标记
}Node;
//结构体_图
typedef struct Graph {
Arc AdjMatrix[MAX_NUM][MAX_NUM]; //邻接矩阵
Node Nodes[MAX_NUM]; //节点数组
int Arc_Num; //弧边数量
int Node_Num; //节点数量
}Graph;
//初始化函数
bool InitGraph(Graph &G)
{
G.Arc_Num = 8;
G.Node_Num = 7;
for (int i = 0; i < G.Node_Num; i++) {
G.Nodes[i].visited = false;
G.Nodes[i].index = i;
G.Nodes[i].name = (char)('A' + i);
}
for (int i = 0; i < G.Node_Num; i++) {
for (int j = 0; j < G.Node_Num; j++) {
G.AdjMatrix[i][j] = { "NULL",0 };
}
}
G.AdjMatrix[0][5] = G.AdjMatrix[5][0] = { "v6",1 };
G.AdjMatrix[0][6] = G.AdjMatrix[6][0] = { "v5",1 };
G.AdjMatrix[1][2] = G.AdjMatrix[2][1] = { "v1",1 };
G.AdjMatrix[1][3] = G.AdjMatrix[3][1] = { "v2",1 };
G.AdjMatrix[1][4] = G.AdjMatrix[4][1] = { "v3",1 };
G.AdjMatrix[1][6] = G.AdjMatrix[6][1] = { "v4",1 };
G.AdjMatrix[3][6] = G.AdjMatrix[6][3] = { "v7",1 };
G.AdjMatrix[4][5] = G.AdjMatrix[5][4] = { "v8",1 };
return true;
}
//打印邻接矩阵
bool PrintAdjMatrix(Graph G)
{
for (int i = 0; i < G.Node_Num; i++) {
for (int j = 0; j < G.Node_Num; j++) {
cout << G.AdjMatrix[i][j].weight << " ";
}
cout << endl;
}
return true;
}
//打印节点信息
bool PrintNodeInfo(Node &n)
{
n.visited = true;
cout << n.name << " ";
return true;
}
//深度优先搜索 时间复杂度: O(Arc_num+Node_num)
void DFSTraverse(Graph &G, int n, bool(*Visit)(Node &N))
{
//参数n表示由第n号节点开始遍历
Visit(G.Nodes[n]);
for (int i = 0; i < G.Node_Num; i++) {
//若存在通路且另一节点未访问过
if (G.AdjMatrix[n][i].weight > 0 && !G.Nodes[i].visited) {
//打印弧边信息
cout << G.AdjMatrix[n][i].name << " ";
//递归访问
DFSTraverse(G, i, PrintNodeInfo);
}
}
}
//广度优先搜索 时间复杂度: O(Arc_num+Node_num)
void BFSTraverse(Graph &G, int n, bool(*Visit)(Node &N))
{
Node u;
queue<Node> q; //辅助队列
Visit(G.Nodes[n]);
q.push(G.Nodes[n]); //入队
while (!q.empty()) {
//返回队首元素,并出队
u = q.front();
q.pop();
for (int i = 0; i < G.Node_Num; i++) {
//u.index 元素的序号
if (G.AdjMatrix[u.index][i].weight > 0 && !G.Nodes[i].visited) {
//打印弧边信息
cout << G.AdjMatrix[u.index][i].name << " ";
//访问
Visit(G.Nodes[i]);
//入队
q.push(G.Nodes[i]);
}
}
}
}
int main()
{
Graph G;
//邻接矩阵
InitGraph(G); cout << endl << "邻接矩阵: " << endl;
PrintAdjMatrix(G); cout << endl;
//DFS算法
InitGraph(G); cout << "DFS: ";
DFSTraverse(G, 0, PrintNodeInfo); cout << endl;
//BFS算法
InitGraph(G); cout << "BFS: ";
BFSTraverse(G, 0, PrintNodeInfo); cout << endl;
return 0;
}
图的信息可以在 InitGraph 函数里进行修改 。
选择合适的存储结构,编写程序,建立图结构,并求出其连通域个数、各连通域的节点与弧边。
//结构体_弧边
typedef struct Arc {
Info name; //弧边名称
int weight; //弧边权重
}Arc;
//结构体_节点
typedef struct Node {
Info name; //节点名称
int index; //节点序号
int connect; //连通域序号
bool visited; //访问标记
}Node;
//结构体_图
typedef struct Graph {
Arc AdjMatrix[MAX_NUM][MAX_NUM]; //邻接矩阵
Node Nodes[MAX_NUM]; //节点数组
int Arc_Num; //弧边数量
int Node_Num; //节点数量
}Graph;
增加了 connect 连通域序号标记 。
//打印节点信息
bool PrintNodeInfo(Node &n, int count)
{
n.visited = true;
n.connect = count; //标记连通域
cout << n.name << " ";
return true;
}
//深度优先搜索
void DFSTraverse(Graph &G, int n, int count)
{
PrintNodeInfo(G.Nodes[n], count);
for (int i = 0; i < G.Node_Num; i++) {
if (G.AdjMatrix[n][i].weight > 0 && !G.Nodes[i].visited) {
cout << G.AdjMatrix[n][i].name << " ";
DFSTraverse(G, i, count);
}
}
}
//计算图的连通域
int Connection(Graph &G)
{
//连通域计数
int count = 0;
//对节点数组遍历
for (int i = 0; i < G.Node_Num; i++) {
if (G.Nodes[i].visited) continue; //若已访问则跳过
else {
cout << "连通域" << ++count << ":";
DFSTraverse(G, i, count); //利用深度优先搜索标记连通域
cout << endl;
}
}
return count;
}
计算连通域个数的方法很简单,对图的结点数组进行遍历,利用深度优先搜索对结点的 connect 进行标记 。
#define MAX_NUM 20 //最大节点数量
#define Info string //弧边与节点的信息
#include
#include
using namespace std;
//结构体_弧边
typedef struct Arc {
Info name; //弧边名称
int weight; //弧边权重
}Arc;
//结构体_节点
typedef struct Node {
Info name; //节点名称
int index; //节点序号
int connect; //连通域序号
bool visited; //访问标记
}Node;
//结构体_图
typedef struct Graph {
Arc AdjMatrix[MAX_NUM][MAX_NUM]; //邻接矩阵
Node Nodes[MAX_NUM]; //节点数组
int Arc_Num; //弧边数量
int Node_Num; //节点数量
}Graph;
//初始化
bool InitGraph(Graph &G)
{
G.Arc_Num = 13;
G.Node_Num = 13;
for (int i = 0; i < G.Node_Num; i++) {
G.Nodes[i].visited = false;
G.Nodes[i].connect = 0;
G.Nodes[i].index = i;
G.Nodes[i].name = (char)('A' + i);
}
for (int i = 0; i < G.Node_Num; i++) {
for (int j = 0; j < G.Node_Num; j++) {
G.AdjMatrix[i][j] = { "NULL",0 };
}
}
G.AdjMatrix[0][1] = G.AdjMatrix[1][0] = { "v1",1 };
G.AdjMatrix[0][2] = G.AdjMatrix[2][0] = { "v2",1 };
G.AdjMatrix[0][5] = G.AdjMatrix[5][0] = { "v3",1 };
G.AdjMatrix[0][11] = G.AdjMatrix[11][0] = { "v4",1 };
G.AdjMatrix[1][12] = G.AdjMatrix[12][1] = { "v5",1 };
G.AdjMatrix[3][4] = G.AdjMatrix[4][3] = { "v6",1 };
G.AdjMatrix[6][7] = G.AdjMatrix[7][6] = { "v7",1 };
G.AdjMatrix[6][8] = G.AdjMatrix[8][6] = { "v8",1 };
G.AdjMatrix[6][10] = G.AdjMatrix[10][6] = { "v9",1 };
G.AdjMatrix[7][10] = G.AdjMatrix[10][7] = { "v10",1 };
G.AdjMatrix[9][11] = G.AdjMatrix[11][9] = { "v11",1 };
G.AdjMatrix[9][12] = G.AdjMatrix[12][9] = { "v12",1 };
G.AdjMatrix[11][12] = G.AdjMatrix[12][11] = { "v13",1 };
return true;
}
//打印节点信息
bool PrintNodeInfo(Node &n, int count)
{
n.visited = true;
n.connect = count; //标记连通域
cout << n.name << " ";
return true;
}
//深度优先搜索
void DFSTraverse(Graph &G, int n, int count)
{
PrintNodeInfo(G.Nodes[n], count);
for (int i = 0; i < G.Node_Num; i++) {
if (G.AdjMatrix[n][i].weight > 0 && !G.Nodes[i].visited) {
cout << G.AdjMatrix[n][i].name << " ";
DFSTraverse(G, i, count);
}
}
}
//计算图的连通域
int Connection(Graph &G)
{
//连通域计数
int count = 0;
//对节点数组遍历
for (int i = 0; i < G.Node_Num; i++) {
if (G.Nodes[i].visited) continue; //若已访问则跳过
else {
cout << "连通域" << ++count << ":";
DFSTraverse(G, i, count); //利用深度优先搜索标记连通域
cout << endl;
}
}
return count;
}
int main()
{
Graph G; InitGraph(G);
cout << endl; int count = Connection(G);
cout << endl << "该图连通域共有" << count << "个." << endl;
return 0;
}
同样的,图的信息可以在 InitGraph 函数里修改。
编写程序,实现某个图结构的 最小生成树,
利用 Prim 和 Kruskal 两种算法,将树的节点与弧边打出。
//结构体_弧边
typedef struct Arc {
Info name; //弧边名称
int weight; //弧边权重
int x, y; //弧边在邻接矩阵中的坐标
}Arc;
//结构体_节点
typedef struct Node {
Info name; //节点名称
int index; //节点序号
int visited; //访问标记
}Node;
//结构体_图
typedef struct Graph {
Arc AdjMatrix[MAX_NUM][MAX_NUM]; //邻接矩阵
Node Nodes[MAX_NUM]; //节点数组
int Arc_Num; //弧边数量
int Node_Num; //节点数量
}Graph;
为弧边增加了在邻接矩阵内的位置坐标x、y,也就是连接的两个结点的序号。
//---------------------------------------------------------------
//Prim算法 时间复杂度:O(Node_Num^2)
int MiniSpanTree_Prim(Graph &G, int n)
{
//用于存储与生成树连通域相邻的边
vector<Arc> CloseArcs;
int count = G.Node_Num; //循环次数
int weight = 0; //权重
//由第n号节点开始生成最小树
G.Nodes[n].visited = true;
//由第p号节点开始扩展边
int p = n;
while (--count) {
//扩展与生成树连通域相邻的弧边,并存储这些弧边
for (int i = 0; i < G.Node_Num; i++) {
if (G.AdjMatrix[p][i].weight > 0 && G.AdjMatrix[p][i].weight < INT_MAX) {
CloseArcs.push_back(G.AdjMatrix[p][i]);
}
}
//寻找最小权值
int min = INT_MAX;
int k;
//对连通域相邻边集合遍历
for (int i = 0; i < CloseArcs.size(); i++) {
Arc L = CloseArcs[i];
int w = L.weight;
//如果权值小于min大于0,而且连接边的两节点一个已访问一个未访问(防止形成环路)
if (w < min&&w>0 && !(G.Nodes[L.x].visited&&G.Nodes[L.y].visited)) {
min = w;
k = i;
}
}
//增加权值
weight += min;
//最小权值的边,通过该边y坐标找到下一个扩展点
Arc A = CloseArcs[k];
p = A.y;
//将另一未访问节点设为已访问
G.Nodes[p].visited = true;
//打印扩展过程
cout << G.Nodes[A.x].name << "-" << A.name << "-" << G.Nodes[A.y].name << " ";
}
return weight;
}
//-------------------------------------------------------
//修改sort的Compare函数
bool Compare(Arc A1, Arc A2)
{
return A1.weight < A2.weight;
}
//创建查并集
void MakeSet(vector<int> &uset, int n)
{
//将n个0值装到vector容器中
uset.assign(n, 0);
//初始化容器, 第i号元素为i
for (int i = 0; i < n; i++) uset[i] = i;
}
//查找当前元素所在集合的代表元
int FindSet(vector<int> &uset, int u)
{
int i = u;
while (uset[i] != i) i = uset[i];
return i;
}
//Kruskal算法 时间复杂度:O(Arc_Num * log(Arc_Num))
int MiniSpanTree_Kruskal(Graph &G)
{
//权值
int weight = 0;
//标记不同的连通域,用查并集模式存储
vector<int> uset;
MakeSet(uset, G.Node_Num);
//获取矩阵上三角部分,获得所有边的信息
vector<Arc> Arcs;
for (int i = 0; i < G.Node_Num; i++) {
for (int j = i + 1; j < G.Node_Num; j++) {
if (G.AdjMatrix[i][j].weight > 0 && G.AdjMatrix[i][j].weight < INT_MAX) {
Arcs.push_back(G.AdjMatrix[i][j]);
}
}
}
//对各边按权值由小到大排序
sort(Arcs.begin(), Arcs.end(), Compare);
//由小到大遍历
for (int i = 0; i < Arcs.size(); i++) {
//找到边连接两个点分别属于哪个连通域
int e1 = FindSet(uset, Arcs[i].x);
int e2 = FindSet(uset, Arcs[i].y);
//如果连通域不相同
if (e1 != e2) {
//打印生成过程
cout << G.Nodes[Arcs[i].x].name << "-" << Arcs[i].name << "-" << G.Nodes[Arcs[i].y].name << " ";
//权值增加
weight += Arcs[i].weight;
//连通域并集操作(重点)
uset[e1] = e2;
}
}
return weight;
}
将两个连通域归并时利用 查并集操作,读者可以注意一下。
#define MAX_NUM 20 //最大节点数量
#define Info string //弧边与节点的信息
#include
#include
#include
#include
using namespace std;
//结构体_弧边
typedef struct Arc {
Info name; //弧边名称
int weight; //弧边权重
int x, y; //弧边在邻接矩阵中的坐标
}Arc;
//结构体_节点
typedef struct Node {
Info name; //节点名称
int index; //节点序号
int visited; //访问标记
}Node;
//结构体_图
typedef struct Graph {
Arc AdjMatrix[MAX_NUM][MAX_NUM]; //邻接矩阵
Node Nodes[MAX_NUM]; //节点数组
int Arc_Num; //弧边数量
int Node_Num; //节点数量
}Graph;
//初始化函数
bool InitGraph(Graph &G)
{
G.Arc_Num = 10;
G.Node_Num = 6;
for (int i = 0; i < G.Node_Num; i++) {
G.Nodes[i].visited = false;
G.Nodes[i].index = i;
G.Nodes[i].name = (char)('A' + i);
}
for (int i = 0; i < G.Node_Num; i++) {
for (int j = 0; j < G.Node_Num; j++) {
if (i == j) G.AdjMatrix[i][j] = { "NULL",0,i,j };
else G.AdjMatrix[i][j] = { "NULL",INT_MAX,i,j };
}
}
G.AdjMatrix[0][1] = { "v1",6,0,1 }; G.AdjMatrix[1][0] = { "v1",6,1,0 };
G.AdjMatrix[0][2] = { "v3",1,0,2 }; G.AdjMatrix[2][0] = { "v3",1,2,0 };
G.AdjMatrix[0][3] = { "v2",5,0,3 }; G.AdjMatrix[3][0] = { "v2",5,3,0 };
G.AdjMatrix[1][2] = { "v4",5,1,2 }; G.AdjMatrix[2][1] = { "v4",5,2,1 };
G.AdjMatrix[1][4] = { "v6",3,1,4 }; G.AdjMatrix[4][1] = { "v6",3,4,1 };
G.AdjMatrix[2][3] = { "v5",5,2,3 }; G.AdjMatrix[3][2] = { "v5",5,3,2 };
G.AdjMatrix[2][4] = { "v7",6,2,4 }; G.AdjMatrix[4][2] = { "v7",6,4,2 };
G.AdjMatrix[2][5] = { "v8",4,2,5 }; G.AdjMatrix[5][2] = { "v8",4,5,2 };
G.AdjMatrix[3][5] = { "v9",2,3,5 }; G.AdjMatrix[5][3] = { "v9",2,5,3 };
G.AdjMatrix[4][5] = { "v10",6,4,5 }; G.AdjMatrix[5][4] = { "v10",6,5,4 };
return true;
}
//---------------------------------------------------------------
//Prim算法 时间复杂度:O(Node_Num^2)
int MiniSpanTree_Prim(Graph &G, int n)
{
//用于存储与生成树连通域相邻的边
vector<Arc> CloseArcs;
int count = G.Node_Num; //循环次数
int weight = 0; //权重
//由第n号节点开始生成最小树
G.Nodes[n].visited = true;
//由第p号节点开始扩展边
int p = n;
while (--count) {
//扩展与生成树连通域相邻的弧边,并存储这些弧边
for (int i = 0; i < G.Node_Num; i++) {
if (G.AdjMatrix[p][i].weight > 0 && G.AdjMatrix[p][i].weight < INT_MAX) {
CloseArcs.push_back(G.AdjMatrix[p][i]);
}
}
//寻找最小权值
int min = INT_MAX;
int k;
//对连通域相邻边集合遍历
for (int i = 0; i < CloseArcs.size(); i++) {
Arc L = CloseArcs[i];
int w = L.weight;
//如果权值小于min大于0,而且连接边的两节点一个已访问一个未访问(防止形成环路)
if (w < min&&w>0 && !(G.Nodes[L.x].visited&&G.Nodes[L.y].visited)) {
min = w;
k = i;
}
}
//增加权值
weight += min;
//最小权值的边,通过该边y坐标找到下一个扩展点
Arc A = CloseArcs[k];
p = A.y;
//将另一未访问节点设为已访问
G.Nodes[p].visited = true;
//打印扩展过程
cout << G.Nodes[A.x].name << "-" << A.name << "-" << G.Nodes[A.y].name << " ";
}
return weight;
}
//-------------------------------------------------------
//修改sort的Compare函数
bool Compare(Arc A1, Arc A2)
{
return A1.weight < A2.weight;
}
//创建查并集
void MakeSet(vector<int> &uset, int n)
{
//将n个0值装到vector容器中
uset.assign(n, 0);
//初始化容器, 第i号元素为i
for (int i = 0; i < n; i++) uset[i] = i;
}
//查找当前元素所在集合的代表元
int FindSet(vector<int> &uset, int u)
{
int i = u;
while (uset[i] != i) i = uset[i];
return i;
}
//Kruskal算法 时间复杂度:O(Arc_Num * log(Arc_Num))
int MiniSpanTree_Kruskal(Graph &G)
{
//权值
int weight = 0;
//标记不同的连通域,用查并集模式存储
vector<int> uset;
MakeSet(uset, G.Node_Num);
//获取矩阵上三角部分,获得所有边的信息
vector<Arc> Arcs;
for (int i = 0; i < G.Node_Num; i++) {
for (int j = i + 1; j < G.Node_Num; j++) {
if (G.AdjMatrix[i][j].weight > 0 && G.AdjMatrix[i][j].weight < INT_MAX) {
Arcs.push_back(G.AdjMatrix[i][j]);
}
}
}
//对各边按权值由小到大排序
sort(Arcs.begin(), Arcs.end(), Compare);
//由小到大遍历
for (int i = 0; i < Arcs.size(); i++) {
//找到边连接两个点分别属于哪个连通域
int e1 = FindSet(uset, Arcs[i].x);
int e2 = FindSet(uset, Arcs[i].y);
//如果连通域不相同
if (e1 != e2) {
//打印生成过程
cout << G.Nodes[Arcs[i].x].name << "-" << Arcs[i].name << "-" << G.Nodes[Arcs[i].y].name << " ";
//权值增加
weight += Arcs[i].weight;
//连通域并集操作(重点)
uset[e1] = e2;
}
}
return weight;
}
//--------------------------------------------
int main()
{
Graph G;
InitGraph(G); cout << endl << "Prim:";
int w = MiniSpanTree_Prim(G, 0); cout << "最小生成树权值:" << w << endl;
InitGraph(G); cout << endl << "Kruskal: ";
w = MiniSpanTree_Kruskal(G); cout << "最小生成树权值:" << w << endl;
return 0;
}
图的信息可在 InitGraph 里修改。
选择合适的存储结构存储某个有向图,编写程序,计算某一节点到其他节点各自的最短路径是多少。
//结构体_弧边
typedef struct Arc {
Info name; //弧边名称
int weight; //弧边权重
int x, y; //弧边在邻接矩阵的坐标
}Arc;
//结构体_节点
typedef struct Node {
Info name; //节点名称
int index; //节点序号
int visited; //访问标记
}Node;
//结构体_图
typedef struct Graph {
Arc AdjMatrix[MAX_NUM][MAX_NUM]; //邻接矩阵
Node Nodes[MAX_NUM]; //节点数组
int Arc_Num; //弧边数量
int Node_Num; //节点数量
}Graph;
本实验采用经典的 Dijkstra 算法 计算最短路径:
//Dijkstra算法 时间复杂度:O(Node_Num^2) 计算从v0点到图中其余各点的最短路径
void ShortestPath_DIJ(Graph &G, int v0)
{
//Distance[i]表示v0点到i点的最短距离
int Distance[MAX_NUM];
//Final[i]表示是否已求出v0点到i点的最短距离
bool Final[MAX_NUM];
//初始化
for (int i = 0; i < G.Node_Num; i++) {
Final[i] = false;
Distance[i] = G.AdjMatrix[v0][i].weight;
}
//初始化,v0点属于连通集S
Final[v0] = true;
int v; int min;
//剩余G.Node_Num-1个点待计算
for (int i = 1; i < G.Node_Num; i++) {
//为防止溢出,min不再使用INT_MAX
min = 10000;
for (int j = 0; j < G.Node_Num; j++) {
//如果点j属于G-S,且与连通域有通路
if (!Final[j] && Distance[j] < min) {
//计算最短路径
min = Distance[j];
//若有最短路径则用v标记改点
v = j;
}
}
//将v点加入连通集S
Final[v] = true;
//更新当前最短路径
for (int w = 0; w < G.Node_Num; w++) {
//对于G-S的点x,Distance[x] = min{ D(v0,v) + D(v,x) , D(v0,x) }
if (!Final[w] && (min + G.AdjMatrix[v][w].weight < Distance[w])) {
Distance[w] = min + G.AdjMatrix[v][w].weight;
}
}
}
//打印到各点的距离
cout << G.Nodes[v0].name << "点" << endl;
for (int i = 0; i < G.Node_Num; i++) {
cout << "到" << G.Nodes[i].name << "最短距离: ";
if (Distance[i] >= 10000) cout << "M" << endl;
else cout << Distance[i] << endl;
}
}
#define MAX_NUM 20 //最大节点数量
#define Info string //弧边与节点的信息
#include
#include
#include
#include
using namespace std;
//结构体_弧边
typedef struct Arc {
Info name; //弧边名称
int weight; //弧边权重
int x, y; //弧边在邻接矩阵的坐标
}Arc;
//结构体_节点
typedef struct Node {
Info name; //节点名称
int index; //节点序号
int visited; //访问标记
}Node;
//结构体_图
typedef struct Graph {
Arc AdjMatrix[MAX_NUM][MAX_NUM]; //邻接矩阵
Node Nodes[MAX_NUM]; //节点数组
int Arc_Num; //弧边数量
int Node_Num; //节点数量
}Graph;
//初始化函数
bool InitGraph(Graph &G)
{
G.Arc_Num = 8;
G.Node_Num = 6;
for (int i = 0; i < G.Node_Num; i++) {
G.Nodes[i].visited = false;
G.Nodes[i].index = i;
G.Nodes[i].name = (char)('A' + i);
}
for (int i = 0; i < G.Node_Num; i++) {
for (int j = 0; j < G.Node_Num; j++) {
if (i == j) G.AdjMatrix[i][j] = { "NULL",0,i,j };
else G.AdjMatrix[i][j] = { "NULL",10000,i,j }; //为防止溢出,不使用INT_MAX
}
}
G.AdjMatrix[0][2] = { "v4",10,0,2 };
G.AdjMatrix[0][4] = { "v3",30,0,4 };
G.AdjMatrix[0][5] = { "v1",100,0,5 };
G.AdjMatrix[1][2] = { "v8",5,1,2 };
G.AdjMatrix[2][3] = { "v7",50,2,3 };
G.AdjMatrix[3][5] = { "v5",10,3,5 };
G.AdjMatrix[4][3] = { "v6",20,4,3 };
G.AdjMatrix[4][5] = { "v2",60,4,5 };
return true;
}
//Dijkstra算法 时间复杂度:O(Node_Num^2) 计算从v0点到图中其余各点的最短路径
void ShortestPath_DIJ(Graph &G, int v0)
{
//Distance[i]表示v0点到i点的最短距离
int Distance[MAX_NUM];
//Final[i]表示是否已求出v0点到i点的最短距离
bool Final[MAX_NUM];
//初始化
for (int i = 0; i < G.Node_Num; i++) {
Final[i] = false;
Distance[i] = G.AdjMatrix[v0][i].weight;
}
//初始化,v0点属于连通集S
Final[v0] = true;
int v; int min;
//剩余G.Node_Num-1个点待计算
for (int i = 1; i < G.Node_Num; i++) {
//为防止溢出,min不再使用INT_MAX
min = 10000;
for (int j = 0; j < G.Node_Num; j++) {
//如果点j属于G-S,且与连通域有通路
if (!Final[j] && Distance[j] < min) {
//计算最短路径
min = Distance[j];
//若有最短路径则用v标记改点
v = j;
}
}
//将v点加入连通集S
Final[v] = true;
//更新当前最短路径
for (int w = 0; w < G.Node_Num; w++) {
//对于G-S的点x,Distance[x] = min{ D(v0,v) + D(v,x) , D(v0,x) }
if (!Final[w] && (min + G.AdjMatrix[v][w].weight < Distance[w])) {
Distance[w] = min + G.AdjMatrix[v][w].weight;
}
}
}
//打印到各点的距离
cout << G.Nodes[v0].name << "点" << endl;
for (int i = 0; i < G.Node_Num; i++) {
cout << "到" << G.Nodes[i].name << "最短距离: ";
if (Distance[i] >= 10000) cout << "M" << endl;
else cout << Distance[i] << endl;
}
}
int main()
{
Graph G;
InitGraph(G);
//DIJ算法
cout << endl;
ShortestPath_DIJ(G, 0);
return 0;
}
图的信息可在 InitGraph 函数里修改,起始结点通过可以修改 v0 参数实现。
声明:本文内容来源于武汉理工大学2019-2020学年数据结构与算法课程实验,仅供学习参考。如有不足地方,还请指出。
代码不要无脑抄 ,建议理解思路。祝愿读者能够在编程之路上不断进步!