c++ 模板编程 图结构设计及最短路求解 算法及代码

图的定义

以蓝桥杯题目《最短路》为例
在这里插入图片描述

解法与思考

以上图为例,我们可以发现图由顶点(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;
}

输出

在这里插入图片描述

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值