数据结构:图(附C++实现代码)

图的定义

  • 由非空顶点集V和边集E组成,记为 G = ( V , E ) G=(V,E) G=(V,E) E ( G ) E(G) E(G)表示图G中顶点间的关系几何, E = { ( u , v ) ∣ u ∈ V , v ∈ V } E=\left \{ (u,v) | u \in V, v \in V \right \} E={(u,v)uV,vV}。根据边是否有方向分为无向图和有向图。根据是否有重复边可以分为简单图和多重图。
    在这里插入图片描述
  • 相关定义:
    • 依附于顶点的边的个数,被称为顶点的度;
    • 定点间如果有可达路径,被称为连通,如果两个顶点互相都可达,称为强连通。对于无向图,如果任意顶点间都是连通的,称为连通图。对于有向图,如果任意间都是强连通的称为强连通图
    • 对于边较多的称为稠密图,边较少的称为稀疏图,二者没有明确边界

图的存储方法

  • 邻接矩阵法:存储一个 n × n n \times n n×n的矩阵,点 ( i , j ) (i,j) (i,j)代表顶点i与顶点j之间有无边,无向图的邻接矩阵是对称矩阵。如果是有权图,矩阵中可以存储边的权值。这种方法复杂度为 O ( n 2 ) O(n^2) O(n2)比较适合存储稠密图,
    在这里插入图片描述

    • 如果用0来表示无边,1表示有边,邻接矩阵A的n次方中的点(i,j)表示从i到j长度为n的路径数量。
  • 邻接表:定义顶点类和边类,顶点类存储顶点信息维护一个指针指向边类,边类存储边的信息维护一个指针指向下一条边。与树的孩子表示法类似。
    在这里插入图片描述

    • 复杂度 O ( V + E ) O(V+E) O(V+E),适合存储稀疏图,找指向某个顶点的所有边不太方便。
  • 十字链表(只能用于存储有向图):跟上面的邻接表类似,在尾节点相同的边之间也维持了一个链表
    在这里插入图片描述

  • 邻接多重表(无向图):跟十字链表类似
    在这里插入图片描述

图的相关操作

在这里插入图片描述

图的应用

  • 深度学习:神经网络在训练时需要进行反向传播更新梯度,可以使用图的形式存储神经网络,每个中间数据就是图的一个节点,每个操作就是一条边,这样设计便于进行i求导和梯度更新。在推理时,可以将图中可以合并的多个边和节点合并成一个,提高计算效率,这一步骤称为模型推理的图优化阶段。例如 z = s i g m o d ( y ) , y = w x + b z=sigmod(y),y=wx+b z=sigmod(y),y=wx+b,可以合并成 z = s i g m o d ( w x + b ) z=sigmod(wx+b) z=sigmod(wx+b)
  • 自动驾驶:路径规划时,可以将路径空间建模为一个图,在图中查找满足约束的最优路径,将路径规划问题建模为查找图中最短路径的问题。

C++代码实现

邻接矩阵存储

#include <iostream>
#include <vector>
#include <unordered_map>

using namespace std;

constexpr int kNoEdge = 0;
enum class GraphType{
    kDigraph,
    kUndigraph,
};

/// @brief 邻接矩阵法实现,临界矩阵中0表示无边,其他表示权重
template<typename T>
class Graph{
public:
    Graph(vector<T> vertexs, vector<vector<int>> edges, GraphType graphtype) : vertexs_(vertexs), edges_(edges), graphtype_(graphtype) {
        for(int i=0;i<vertexs_.size();i++){
            vertex_indexs_[vertexs_[i]] = i;
        }
    }
    bool Adjacent(T x,T y) const;
private:
    GraphType graphtype_;
    vector<T> vertexs_;
    unordered_map<T, int> vertex_indexs_;
    vector<vector<int>> edges_;
};

template<typename T>
bool Graph<T>::Adjacent(T x, T y) const
{
    auto iter = vertex_indexs_.find(x);
    if(iter == vertex_indexs_.end()){
        cout << "Error Vertex" << endl;
        return false;
    }
    int index1 =  iter->second;
    iter = vertex_indexs_.find(y);
    if(iter == vertex_indexs_.end()){
        cout << "Error Vertex" << endl;
        return false;        
    }
    int index2 = iter->second;
    return edges_[index1][index2];
}

int main()
{
    vector<string> vertexs = {"home","school","KTV"};
    vector<vector<int>> edges = {{0,1,1},{1,0,0},{1,0,0}};
    Graph<string> test_map(vertexs,edges,MapType::kUndigraph);
    cout << test_map.Adjacent("school","KTV") << endl;
    cout << test_map.Adjacent("school","home") << endl;
    return 0;
}

邻接表实现

#include <iostream>
#include <vector>
#include <unordered_map>
#include <string>
#include <memory>

using namespace std;

enum class MapType{
    kDigraph,
    kUndigraph
};

class Edge{
public:

    Edge(int start_vertex_id,int end_vertex_id) : 
        value_(1.0),
        next_edge_(nullptr),
        start_vertex_id_(start_vertex_id),
        end_vertex_id_(end_vertex_id) {}
    ~Edge() {
        if(next_edge_ != nullptr){
            delete next_edge_;
        }
    };
// private:
    double value_;
    int start_vertex_id_;
    int end_vertex_id_;
    Edge* next_edge_;
};

// 定义图中顶点的数据结构
template<typename T>
class Vertex {
public:
    Vertex(T data) : data_(data) {}
    Vertex(T data, int id) : data_(data), vertex_id_(id) {}
    bool AddEdge(int end_vertex_id){
        if(edges_ == nullptr){
            edges_ = make_shared<Edge>(vertex_id_,end_vertex_id);
        }else{
            Edge* temp = edges_.get();
            while(temp->next_edge_ != nullptr){
                if(temp->end_vertex_id_ == end_vertex_id) { return false;}
                temp = temp->next_edge_;
            }
            if(temp->end_vertex_id_ == end_vertex_id) { return false;}
            temp->next_edge_ = new Edge(vertex_id_,end_vertex_id);
        }
        return true;
    }

    bool Adjacent(int end_vertex_id){
        Edge* temp = edges_.get();
        while(temp != nullptr){
            if(temp->end_vertex_id_ == end_vertex_id) { return true;}
            temp = temp->next_edge_;
        }
        return false;
    }
// private:
    T data_;
    int vertex_id_;
    shared_ptr<Edge> edges_;
};

template<typename T>
class Graph {
public:
    Graph() : type_(MapType::kDigraph) {};
    bool AddVertex(Vertex<T> input){
        if(vertex_index_.find(input.data_) != vertex_index_.end()){return false;}
        vertex_index_[input.data_] = vertexs_.size();
        input.vertex_id_ = vertexs_.size();
        vertexs_.emplace_back(input);
        return true;
    }
    bool AddEdge(T start, T end){
        auto iter = vertex_index_.find(start);
        if(iter == vertex_index_.end()){return false;}
        int start_id = iter->second;
        iter = vertex_index_.find(end);
        if(iter == vertex_index_.end()){return false;}
        return vertexs_[start_id].AddEdge(iter->second);
    }
    bool Adjacent(T start, T end){
        auto iter = vertex_index_.find(start);
        if(iter == vertex_index_.end()){return false;}
        int start_id = iter->second;
        iter = vertex_index_.find(end);
        if(iter == vertex_index_.end()){return false;}
        return vertexs_[start_id].Adjacent(iter->second);
    }
// private:
    MapType type_;
    vector<Vertex<T>> vertexs_;
    unordered_map<T,int> vertex_index_;

};

int main() {
    Vertex<string> home("home");
    Vertex<string> school("school");
    Vertex<string> KTV("KTV");
    Graph<string> graph;
    graph.AddVertex(home);
    graph.AddVertex(school);
    graph.AddVertex(KTV);
    graph.AddEdge("home","school");
    graph.AddEdge("home","KTV");
    cout << graph.Adjacent("home","school") << endl;
    return 0;
}

Ref

王道考研408-数据结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值