图的定义
以蓝桥杯题目《最短路》为例
解法与思考
以上图为例,我们可以发现图由顶点(vertex)和边(edge)组成,且一个边对应两个顶点。那么我们可以把它的结构抽象出来,也就是说图要实现这么几个功能:
1.已知两点必然知道一边
2.已知一点必然知道其所对应的子节点
3.图的必须能够一次插入两点一边
4.顶点和边是不同的类
5.顶点和边的类型定义是灵活的
由上面几个功能我们可以知道,我们的图必须得使用map的形式进行构建,且参数类型是可以灵活定义的,这就要求我们得通过模板元编程来构建我们的图结构,而map必须指向另外一个map。
图结构定义
图(Graph)类的定义如下
#include <iostream>
#include <map>
#include <vector>
//T对应顶点的类型,U对应边的类型
template<typename T,typename U>
class Graph
{
private:
std::map<T,std::map<T, U>> graphMap; //数据存储
public:
//添加边和结点的三种样式
void addEdge(T vertex1, T vertex2, U edge, std::string mode = "undirected");//单边插入,默认无向图模式
void addEdge(T vertex1, std::vector<T> vertex2, std::vector<U> edge, std::string mode = "undirected");//一对多插入
void addEdge(std::vector<T> vertex1,std::vector<T> vertex2, std::vector<U> edge, std::string mode = "undirected");//多对多插入
std::vector<T> getVertex(); //获得所有节点
//获得边
std::vector<U> getEdge(); //获得所有边
U getEdge(T vertex1, T vertex2); //获得指定边
std::vector<T> getChildVertex(T vertex); //获得所有子节点
int getSize() { return graphMap.size(); }; //获得图的大小
};
对结构函数的声明
//----------------------------------------------//对函数的定义
template<typename T,typename U>
void Graph<T, U>::addEdge(T vertex1, T vertex2, U edge, std::string mode)
{
graphMap[vertex1][vertex2] = edge;
if (mode == "undirected")
{
graphMap[vertex2][vertex1] = edge;
}
}
template<typename T, typename U>
void Graph<T, U>::addEdge(T vertex1, std::vector<T> vertex2, std::vector<U> edge, std::string mode)
{
if (vertex2.size() != edge.size() )
{
std::cout << "Error!Size of vertex2 and edge must be same!" << std::endl;
}
else
{
for (int i = 0;i < edge.size();i++)
{
graphMap[vertex1][vertex2[i]] = edge[i];
if (mode == "undirected")
{
graphMap[vertex2[i]][vertex1] = edge[i];
}
}
}
}
template<typename T, typename U>
void Graph<T, U>::addEdge(std::vector<T> vertex1, std::vector<T> vertex2, std::vector<U> edge, std::string mode)
{
if (vertex1.size() != vertex2.size() or vertex2.size!=edge.size())
{
std::cout << "Error!Size of vertex or edge must be same!" << std::endl;
}
else
{
for(int i=0;i< edge.size();i++)
{
graphMap[vertex1[i]][vertex2[i]] = edge[i];
if(mode == "undirected")
{
graphMap[vertex2[i]][vertex1[1]] = edge[i];
}
}
}
}
template<typename T, typename U>
std::vector<T> Graph<T, U>::getVertex()
{
std::vector<T> keys;
//注意iterator前面得加typename,否则会报错
for (typename std::map<T, std::map<T, U>>::iterator it = graphMap.begin();it != graphMap.end(); ++it)
{
keys.push_back(it->first);
}
return keys;
}
template<typename T, typename U>
std::vector<U> Graph<T, U>::getEdge()
{
std::vector<U> values;
for (typename std::map<T, std::map<T, U>>::iterator it = graphMap.begin(); it != graphMap.end(); ++it)
{
values.push_back(it->second);
}
return values;
}
template<typename T, typename U>
U Graph<T, U>::getEdge(T vertex1, T vertex2)
{
return graphMap[vertex1][vertex2];
}
template<typename T, typename U>
std::vector<T> Graph<T, U>::getChildVertex(T vertex)
{
std::vector<T> keys;
for (typename std::map<T, U>::iterator it = graphMap[vertex].begin(); it != graphMap[vertex].end(); ++it)
{
keys.push_back(it->first);
}
return keys;
}
最短路求解方法
算法的虚函数抽象
输入一个邻接矩阵,返回一个最短路邻接矩阵,solve为求解过程
template<typename T>
struct ShortestPathAlgorithm {
virtual void solve(std::vector<std::vector<T>> &roadArray)=0;
};
1.Floyd算法
该算法的核心步骤如下:
a.初始化邻接矩阵,点自身的路径值设为0,无直接相连的点的边值设为正无穷(一个极大值,方便比较)
b.三重遍历,思想就是使用另一个点得到三点之间的距离,与两点的距离比较,若小于,则将此较小值与其兑换,得到最短路径
代码:
template<typename T>
class FloydAlgorithm:public ShortestPathAlgorithm<T>
{
void solve(std::vector<std::vector<T>> &roadArray)
{
int length = roadArray.size();
for (int k = 0;k < length;k++)
{
for (int i = 0;i < length;i++)
{
for (int j = 0;j < length;j++)
{
if (roadArray[i][k] + roadArray[k][j] < roadArray[i][j])
{
roadArray[i][j] = roadArray[i][k] + roadArray[k][j];
}
}
}
}
}
};
本文求解步骤
功能代码
打印二位数组的代码:
void printArray(std::vector<std::vector<int>> array)
{
for (int i = 0;i < array.size();i++)
{
for (int j = 0;j < array[i].size();j++)
{
std::cout << array[i][j] << " ";
}
std::cout << std::endl;
}
}
求解函数:
void graphFunction(std::string beginVertex="A",std::string endVertex="S",std::string solveMethod="Floyd")
{
//插入顶点和边
Graph<std::string, int> graph;
graph.addEdge("A", { "B","C","D","E" }, { 3,1,1,1 });
graph.addEdge("B", { "A","G","J" }, { 2,1,2 });
graph.addEdge("C", { "A","D","G","F" }, { 1,3,3,3 });
graph.addEdge("D", { "A","E","I","H","G","C" }, { 1,1,2,1,2,3 });
graph.addEdge("E", { "A","D","H","I" }, { 1,1,1,3 });
graph.addEdge("F", { "C","G","J" }, { 3,1,1 });
graph.addEdge("G", { "B","C","D","K","F" }, { 1,3,2,2,1 });
graph.addEdge("H", { "D","E","I","L" }, { 1, 1, 1, 2 });
graph.addEdge("I", { "H","D","E","M" }, { 1,2,3,3 });
graph.addEdge("J", { "B","F","S" }, { 2,1,2 });
graph.addEdge("K", { "G","L","P","N" }, { 2,3,2,1 });
graph.addEdge("L", { "K","H","M","R" }, { 3,2,1,1 });
graph.addEdge("M", { "L","I","S","Q" }, { 1,3,1,1 });
graph.addEdge("N", { "K","M","P" }, { 1,2,1 });
graph.addEdge("O", { "Q","R","P" }, { 1,3,1 });
graph.addEdge("P", { "N","O","K" }, { 1,2,2 });
graph.addEdge("Q", { "O","M" }, { 1,1 });
graph.addEdge("R", { "O","L","S" }, { 3,1,1 });
graph.addEdge("S", { "J","M","R" }, { 2,1,1 });
int size = graph.getSize();
std::cout <<"graph size:"<< size << std::endl;
std::vector<std::string> vertexs = graph.getVertex();
std::vector<std::vector<int>> roadArray(vertexs.size(), std::vector<int>(vertexs.size(), 100000));
//初始化邻接矩阵
for (int i = 0;i < vertexs.size();i++)
{
std::vector<std::string>childVertex = graph.getChildVertex(vertexs[i]);
for (int j = 0;j < vertexs.size();j++)
{
int num = graph.getEdge(vertexs[i], vertexs[j]);
if (num == NULL)
continue;
else
roadArray[i][j] = graph.getEdge(vertexs[i], vertexs[j]);
}
roadArray[i][i] = 0;
}
int length = roadArray.size();
int num = 0;
//选择算法
if (solveMethod == "Floyd")
{
ShortestPathAlgorithm<int> *algorithm = new FloydAlgorithm<int>();
algorithm->solve(roadArray);
}
printArray(roadArray);
std::vector<int>location(2);
//获取索引
for (int i = 0;i < vertexs.size();i++) {
if (vertexs[i] == beginVertex)
{
location[0] = i;
}
if (vertexs[i] == endVertex)
{
location[1] = i;
}
}
std::cout << "value:" << roadArray[location[0]][location[1]] << std::endl;
}
int main()
{
graphFunction();
return 0;
}