基于图的邻接链表和邻接矩阵实现图的各种常用函数
图最常用的表示方法有邻接链表和邻接矩阵;图的常见函数包括图的建立和销毁,边的插入删除,图的深度优先和广度优先遍历,最小生成树,最短路径等。下面代码在邻接链表和邻接矩阵的基础上分别实现了函数,其中类Graph 是邻接链表表示的,GraphM是邻接矩阵表示的。这里代码比较多,原因是有可能存在多个版本或者多种实现方法。不多说啦,直接上代码。
文件main.cpp
/*
* 文件 main.cpp
* 功能 示例
* 作者 lmjy
* 日期 2017/5/27
*
*/
#include <iostream>
#include <fstream>
#include <vector>
#include <stack>
#include <queue>
#include "graph.h"
using namespace std;
int main(){
Graph g1(ifstream("g.txt"), 5, 10);
//GraphM g1(ifstream("g.txt"),5, 10);
cout << "输出图: " << endl;
g1.print();
cout << endl;
cout << "深度遍历0: " << g1.dfs1(0) << endl;
cout << "深度遍历3: " << g1.dfs2(3) << endl;
cout << "广度遍历4: " << g1.bfs(4) << endl;
cout << "最小生成树: " << g1.mstPrim() << endl;
cout << "最小生成树: " << g1.mstKruskal() << endl;
cout << "BF最短路径(0,2)" << g1.shortestPathBF(0, 2) << endl;
cout << "DJ最短路径(0,2)" << g1.shortestPathDijkstra(0, 2) << endl;
cout << "BF最短路径(2,0)" << g1.shortestPathBF(2, 0) << endl;
cout << "DJ最短路径(2,0)" << g1.shortestPathDijkstra(2, 0) << endl;
cout << "BF最短路径(1,4)" << g1.shortestPathBF(1, 4) << endl;
cout << "DJ最短路径(1,4)" << g1.shortestPathDijkstra(1, 4) << endl;
cout << "BF最短路径(4,1)" << g1.shortestPathBF(4, 1) << endl;
cout << "DJ最短路径(4,1)" << g1.shortestPathDijkstra(4, 1) << endl;
return 0;
}
其中:图结构如图所示
根据图结构书写格式,g.txt如下
0 0 1 3
1 0 3 5
2 1 2 6
3 1 3 2
4 2 4 2
5 3 1 1
6 3 2 4
7 3 4 6
8 4 2 7
9 4 0 3
运行结果如下(Graph类):
运行结果(GraphM类):
下面是图的具体实现文件
文件 graph.h
/*
* 文件 graph.h
* 功能 图相关的类和函数的声明
* 作者 lmjy
* 日期 2017/5/27
*
*/
#ifndef GRAPH_H
#define GRAPH_H
#define PATHEV 1 // 路径集合是边还是点 1为边 0为点
#define MAXWEIGHT INT32_MAX >> 1 // 表示权值无穷大
typedef int Weight; // 权重
typedef int Status; // 状态记录
typedef int Data[1]; // 数据
struct EdgeNode { // 边结点
int eNum, headv, tailv; // 边编号, 边起点, 边终点
Weight weight; // 边权值
Status status; // 边状态
Data data{ 0 }; // 边数据
EdgeNode *nextIn, *nextOut; // 入边( 十字链表才需要 ), 出边
EdgeNode(int en = 0, int v1 = 0, int v2 = 0, Weight w = 1) // 构造函数, 边编号 起点 尾点 权值
:eNum(en), headv(v1), tailv(v2), weight(w), status(0), nextIn(nullptr), nextOut(nullptr) {}
EdgeNode(std::istream& is) : status(0), nextIn(nullptr), nextOut(nullptr) { // 输入流, 构成一条边 格式: 边编号 起点 尾点 权值
is >> eNum >> headv >> tailv >> weight;}
friend bool operator==(const EdgeNode&, const EdgeNode&); // 相等重载, 判断是否同一条边
friend bool operator!=(const EdgeNode&, const EdgeNode&); // 不等重载
friend std::ostream& operator << (std::ostream&, EdgeNode&); // 输入输出重载, 格式: 边编号 起点 尾点 权值
friend std::istream& operator >> (std::istream&, EdgeNode&);
};
bool operator==(const EdgeNode&, const EdgeNode&); // 边友元函数声明
bool operator!=(const EdgeNode&, const EdgeNode&);
std::ostream& operator<<(std::ostream&, EdgeNode&);
std::istream& operator >> (std::istream&, EdgeNode&);
struct VertexNode { // 点结点
int vNum; // 点编号
Status status; // 点状态
Data data{ 0 }; // 点数据
EdgeNode *nextIn, *nextOut; // 入边, 出边
VertexNode(int n1 = 0) :vNum(n1), status(0), nextIn(nullptr), nextOut(nullptr) {}
};
struct Path {
int vStart, vEnd; // 路径的起止点(源和目的节点)
Weight dist; // 路径总代价
std::vector<int> pathV, pathE; // 存储路径, 分别存中间点编号和边编号
Path(int v1 = 0, int v2 = 0, Weight d = 0) :vStart(v1), vEnd(v2), dist(d) {}
Path& operator+=(const Path&); // 添加路径在末端
friend Path operator+(const Path&, const Path&); // 连接两条路径
friend std::ostream& operator <<(std::ostream&, Path&); // 输出重载 格式: 起点 --> 终点 $ 代价 @ <路径>
};
Path operator+(const Path&, const Path&);
std::ostream& operator <<(std::ostream&, Path&);
struct Graph { // 图类, 邻接链表表示
int vn, en; // 顶点和边的数目
std::vector<VertexNode> adjList; // 邻接链表( 十字链表 )
Graph(int n1 = 0) :vn(n1), en(0), adjList(n1) {
for (int k1(0); k1 < n1; ++k1) adjList[k1].vNum = k1;
}
Graph(std::istream& is, int n1 = 2, int n2 = 1, bool v = true); // n1 点上限, n2 边上限, v表示有向或者无向图
~Graph();
void insert(const EdgeNode&,bool = true); // 插入一条边, v表示有向或者无向图, 默认有向
int remove(const EdgeNode&, bool = true); // 删除一条边, v表示有向或者无向图, 默认有向, 返回删除的边数
int remove(int, bool = true); // 删除一顶点相连的所有边, 返回删除的边数
void print(bool = true, std::ostream& = std::cout);
Path dfs1(int = 0); // 深度遍历递归 遍历顶点依次存在 Path 中
void dfs1(Path&, int = 0); // 深度遍历递归辅助函数
Path dfs2(int = 0); // 深度遍历迭代
Path bfs(int = 0); // 广度遍历迭代
Path mstPrim(int = 0); // 最小生成树 prim
Path mstKruskal(); // 最小生成树 Kruskal
std::vector<Path> shortestPathBF(int = 0); // 最短路径 Bellman Ford 单源
Path shortestPathBF(int, int); // 最短路径 Bellman Ford 两点
std::vector<Path> shortestPathDijkstra(int = 0); // 最短路径 Dijkstra 单源
Path shortestPathDijkstra(int, int); // 最短路径 Dijkstra 两点
};
struct GraphM { // 图类, 邻接矩阵表示
int vn;
std::vector<std::vector<Weight>> wMat; // 邻接矩阵 存储权值, 0表示无穷大
std::vector<std::vector<int>> eMat; // 边编号矩阵 存储边序号
GraphM(int n1 = 0) :vn(n1), wMat(n1, std::vector<Weight>(n1, 0)), eMat(n1, std::vector<Weight>(n1, -1)) {}
GraphM(std::istream& is, int n1 = 2, int n2 = 1, bool v = true); // n1 点上限, n2 边上限, v表示有向或者无向图
bool insert(const EdgeNode&, bool = true); // 插入一条边, v表示有向或者无向图, 默认有向
int remove(const EdgeNode&, bool = true); // 删除一条边, v表示有向或者无向图, 默认有向, 返回删除的边数
int remove(int, bool = true); // 删除一顶点相连的边,