【慢速学数据结构】图篇

介绍

图,是由一个顶点集和一个边集构成的数据结构。

怎么在程序里表示图?有两种方法,邻接矩阵和邻接表。

虽然用邻接矩阵表示非常简单,但它也有些不可忽视的缺点。比如

  • 矩阵的大小不好提前预知,动态数组的话又不好实现。
  • 如果图是稀疏的话,也造成空间浪费。

邻接表就要好很多。

实现

数据结构要怎么设计?先试着去写一下最基本的东西。
顶点。

template<typename Vt, typename Et>
class Vertex
{
public:
    typedef Edge<Et>* EdgeType;
    Vertex();
    virtual ~Vertex();

public:
    Vt vData_;

private:
    std::vector<EdgeType> outputs;
    std::vector<EdgeType> inputs;
};

边。

template<typename T>
class Edge
{
public:
    Edge(Vertex<T> *t, const T& d) : target_(t), eData_(d);
    virtual ~Edge();

public:
    T eData_;
    Vertex<T> *target_;
};

图。

template<typename Vt, typename Et = int>
class Graph
{
public:
    typedef Vertex<Vt,Et>* VertexPtr;
    typedef Edge<Et>* EdgePtr;
    Graph();
    virtual ~Graph();


public:
    void newEdge(VertexPtr source, VertexPtr target, Et data);
    VertexPtr newVertex();

private:
    std::deque<std::shared_ptr<Vertex<Vt, Et> > > vertexList_;
    std::deque<std::shared_ptr<Edge<Et> > > edgeList_;

};

图的遍历

BFS,思路和树的层序遍历是一样的。

    void BFS(VertexPtr &start)
    {
        std::queue<VertexPtr> q;
        std::map<VertexPtr, bool> visit;

        for(auto i: vertexList_)
        {
            visit[i.get()] = false;
        }

        visit[start] = true;
        q.push(start);


        while(!q.empty())
        {
            VertexPtr node =  q.front();
            q.pop();



            for(auto edge : node->outputs)
            {
                if(!visit[edge->target_])
                {
                    visit[edge->target_] = true;
                    q.push(edge->target_);
                }
            }
        } 
    }

DFS,递归。

void DFSHelper(VertexPtr &s, std::map<VertexPtr,bool> &visit)
    {

        if(!s) return;


        for(auto edge : s->outputs)
        {
            if(!visit[edge->target_])
            {
                visit[edge->target_] = true;
                DFSHelper(edge->target_, visit);
            }
        }
    }
    void DFS(VertexPtr &start)
    {
        std::map<VertexPtr,bool> visit;

        for(auto i : vertexList_)
        {
            visit[i.get()] = false;
        }

        DFSHelper(start, visit);
    }

最短路径算法

Dijkstra,他老人家这辈子始终在追求简洁算法之美,为了向他致敬,我也用精炼的语言总结一下:“ 最短路径具有最优子结构性质,自底向上通过子问题的最优解去构建原问题的最优解”


    void DijkstraHelper(VertexPtr &s, std::map<VertexPtr,bool> &visit,
                        std::map<VertexPtr, int> &dist, std::map<VertexPtr, VertexPtr> &path)
    {

        if(!s) return;



        while(1)
        {
            int min = INT_MAX;
            VertexPtr u = nullptr;


            // 找出目前距离最小的结点
            // 这里我遍历了所有的结点,为了提高效率,可以改用优先队列,不过实现方法就需要仔细考虑了
            for(auto v : vertexList_)
            {
                if(dist[v.get()] < min && !visit[v.get()])
                {
                    min = dist[v.get()];
                    u = v.get();
                }
            }


            if(!u) break;

            visit[u] = true;

            for(auto edge : u->outputs)
            {
                if(!visit[edge->target_])
                {            
                    int d = dist[u] + edge->eData_;
                    // 如果当前的距离小于原来的距离,则更新结点的路径,距离信息
                    if(d < dist[edge->target_])
                    {
                        dist[edge->target_] = d;
                        path[edge->target_] = u;
                    }
                }
            }

        }


    }
    void Dijkstra(VertexPtr &start)
    {

        std::map<VertexPtr,bool>        visit;
        std::map<VertexPtr, int>        dist;
        std::map<VertexPtr, VertexPtr>  path;


        // 初始化这三个表
        for(auto i : vertexList_)
        {
            visit[i.get()] = false;
            dist[i.get()] = INT_MAX;
            path[i.get()] = nullptr;
        }

        // 设置好起始结点的信息
        visit[start] = false;
        dist[start] = 0;
        path[start] = nullptr;

        // 执行算法
        DijkstraHelper(start, visit, dist, path);

        // 打印每个结点的,路径、距离信息
        for(auto i : vertexList_)
        {
            if(i.get() == start)
            {
                std::cout<<i->vData_<<" "<<dist[i.get()]<<" nullptr"<<"\n";
                continue;
            }    
            std::cout<<i->vData_<<" "<<dist[i.get()]<<" "<<path[i.get()]->vData_<<"\n";
        }

    }

应用

生活中很多东西用到了图算法,比如地图寻路,路由转发表,等等

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值