zoukankan      html  css  js  c++  java
  • 有权图

    ------------------siwuxie095

       

       

       

       

       

       

       

       

       

    有权图

       

       

    这里介绍有权图(Weighted Graph),所谓有权图,就是图中的

    每一条边上都会有相应的一个或一组值。通常情况下,这个值只是

    一个数字

       

       

    如:在交通运输网中,边上的权值可能表示的是路程,也可能表示

    的是运输费用(显然二者都是数字)。不过,边上的权值也有可能

    是其它东西,比如说是一个字符串,甚至是一个更加复杂的数据包,

    里面集合了更多的数据

       

       

       

    关于有权图的实现,看如下实例:

       

       

       

       

    1)邻接矩阵

       

      

    0

    1

    2

    3

    0

    0

    0.12

    0

    0

    1

    0.12

    0

    0.34

    0.52

    2

    0

    0.34

    0

    0.28

    3

    0

    0.52

    0.28

    0

       

       

    对于邻接矩阵 A 来说,只需要在位置 A[i][j] 处写上相应的权值

    即可,对于没有边的地方,权值为 0

       

       

       

    2)邻接表

       

    0

    {to:1,w:0.12}

      

      

    1

    {to:1,w:0.12}

    {to:2,w:0.34}

    {to:3,w:0.52}

    2

    {to:1,w:0.34}

    {to:3,w:0.28}

      

    3

    {to:1,w:0.52}

    {to:2,w:0.28}

      

       

       

    对于邻接表来说,只需要存储两个信息:

       

    1)相邻的顶点的索引

    2)相应边上的权值

       

    显然,这两个信息不能用一个简单的基本数据类型来表达,因此,

    需要把它们封装成一个类 Edge,即 每个顶点下都是和当前顶点

    相邻的所有边

       

       

       

       

       

    不管是邻接矩阵,还是邻接表,为了保证二者具有统一的接口,

    需要在邻接矩阵的 A[i][j] 处也存储边 Edge,而不仅仅是权值,

    而没有边的地方,存储为 NULL 即可

       

    因为 NULL 的存在,邻接矩阵和邻接表就不能简单存储边 Edge

    而要存储边指针 *Edge

       

       

       

       

       

    程序:

       

    Edge.h:

       

    #ifndef EDGE_H

    #define EDGE_H

       

    #include <iostream>

    #include <cassert>

    using namespace std;

       

       

    //边信息:两个顶点和权值

    template<typename Weight>

    class Edge

    {

       

    private:

       

    int a, b; //边的两个顶点ab(如果是有向图,就默认从顶点a指向顶点b

    Weight weight; //边上的权值

       

    public:

       

    Edge(int a, int b, Weight weight)

    {

    this->a = a;

    this->b = b;

    this->weight = weight;

    }

       

       

    //默认构造函数

    Edge(){}

       

       

    ~Edge(){}

       

       

    int v(){ return a; }

       

       

    int w(){ return b; }

       

       

    Weight wt() { return weight; }

       

       

    //知道边的一个顶点x,返回另一个顶点

    int other(int x)

    {

    assert(x == a || x == b);

    return x == a ? b : a;

    }

       

       

    //友元函数重载

    friend ostream &operator<<(ostream &os, const Edge &e)

    {

    os << e.a << "-" << e.b << ": " << e.weight;

    return os;

    }

       

       

    bool operator<(Edge<Weight> &e)

    {

    return weight < e.wt();

    }

       

       

    bool operator<=(Edge<Weight> &e)

    {

    return weight <= e.wt();

    }

       

       

    bool operator>(Edge<Weight> &e)

    {

    return weight > e.wt();

    }

       

       

    bool operator>=(Edge<Weight> &e)

    {

    return weight >= e.wt();

    }

       

       

    bool operator==(Edge<Weight> &e)

    {

    return weight == e.wt();

    }

    };

       

       

    #endif

       

       

       

    SparseGraph.h:

       

    #ifndef SPARSEGRAPH_H

    #define SPARSEGRAPH_H

       

    #include "Edge.h"

    #include <iostream>

    #include <vector>

    #include <cassert>

    using namespace std;

       

       

       

    // 稀疏图 - 邻接表

    template<typename Weight>

    class SparseGraph

    {

       

    private:

       

    int n, m; //n m 分别表示顶点数和边数

    bool directed; //directed表示是有向图还是无向图

    vector<vector<Edge<Weight> *>> g; //g[i]里存储的就是和顶点i相邻的所有边指针

       

    public:

       

    SparseGraph(int n, bool directed)

    {

    this->n = n;

    this->m = 0;

    this->directed = directed;

    //g[i]初始化为空的vector

    for (int i = 0; i < n; i++)

    {

    g.push_back(vector<Edge<Weight> *>());

    }

    }

       

       

    ~SparseGraph()

    {

       

    for (int i = 0; i < n; i++)

    {

    for (int j = 0; j < g[i].size(); j++)

    {

    delete g[i][j];

    }

    }

    }

       

       

    int V(){ return n; }

    int E(){ return m; }

       

       

    void addEdge(int v, int w, Weight weight)

    {

    assert(v >= 0 && v < n);

    assert(w >= 0 && w < n);

       

    g[v].push_back(new Edge<Weight>(v, w, weight));

    //1)顶点v不等于顶点w,即不是自环边

    //2)且不是有向图,即是无向图

    if (v != w && !directed)

    {

    g[w].push_back(new Edge<Weight>(w, v, weight));

    }

    m++;

    }

       

       

    //hasEdge()判断顶点v和顶点w之间是否有边

    //hasEdge()的时间复杂度:O(n)

    bool hasEdge(int v, int w)

    {

    assert(v >= 0 && v < n);

    assert(w >= 0 && w < n);

       

    for (int i = 0; i < g[v].size(); i++)

    {

    if (g[v][i]->other(v) == w)

    {

    return true;

    }

    }

    return false;

    }

       

       

    void show()

    {

       

    for (int i = 0; i < n; i++)

    {

    cout << "vertex " << i << ": ";

    for (int j = 0; j < g[i].size(); j++)

    {

    cout << "{to:" << g[i][j]->w() << ",wt:" << g[i][j]->wt() << "} ";

    }

    cout << endl;

    }

    }

       

       

       

    //邻边迭代器(相邻,即 adjacent

    //

    //使用迭代器可以隐藏迭代的过程,按照一定的

    //顺序访问一个容器中的所有元素

    class adjIterator

    {

    private:

       

    SparseGraph &G; //图的引用,即要迭代的图

    int v; //顶点v

    int index; //相邻顶点的索引

       

    public:

       

    adjIterator(SparseGraph &graph, int v) : G(graph)

    {

    this->v = v;

    this->index = 0;

    }

       

       

    //要迭代的第一个元素

    Edge<Weight> *begin()

    {

    //因为有可能多次调用begin()

    //所以显式的将index设置为0

    index = 0;

    //如果g[v]size()不为0

    if (G.g[v].size())

    {

    return G.g[v][index];

    }

       

    return NULL;

    }

       

       

    //要迭代的下一个元素

    Edge<Weight> *next()

    {

    index++;

    if (index < G.g[v].size())

    {

    return G.g[v][index];

    }

       

    return NULL;

    }

       

       

    //判断迭代是否终止

    bool end()

    {

    return index >= G.g[v].size();

    }

    };

    };

       

       

    #endif

       

       

       

    DenseGraph.h:

       

    #ifndef DENSEGRAPH_H

    #define DENSEGRAPH_H

       

    #include "Edge.h"

    #include <iostream>

    #include <vector>

    #include <cassert>

    using namespace std;

       

       

       

    // 稠密图 - 邻接矩阵

    template<typename Weight>

    class DenseGraph

    {

       

    private:

       

    int n, m; //n m 分别表示顶点数和边数

    bool directed; //directed表示是有向图还是无向图

    vector<vector<Edge<Weight> *>> g; //二维矩阵,存储边指针

       

    public:

       

    DenseGraph(int n, bool directed)

    {

    this->n = n;

    this->m = 0;

    this->directed = directed;

    //二维矩阵:nn列,全部初始化为NULL

    for (int i = 0; i < n; i++)

    {

    g.push_back(vector<Edge<Weight> *>(n, NULL));

    }

    }

       

       

    ~DenseGraph()

    {

    for (int i = 0; i < n; i++)

    {

    for (int j = 0; j < n; j++)

    {

    if (g[i][j] != NULL)

    {

    delete g[i][j];

    }

    }

    }

    }

       

       

    int V(){ return n; }

    int E(){ return m; }

       

       

    //在顶点v和顶点w之间建立一条边

    void addEdge(int v, int w, Weight weight)

    {

    assert(v >= 0 && v < n);

    assert(w >= 0 && w < n);

       

    //如果顶点v和顶点w之间已经存在一条边,就删掉,

    //之后按照传入权值重建一条边,即直接覆盖

    if (hasEdge(v, w))

    {

    delete g[v][w];

       

    //如果是无向图,还要删除和主对角线对称的值

    if (!directed)

    {

    delete g[w][v];

    }

    m--;

    }

       

    g[v][w] = new Edge<Weight>(v, w, weight);

       

    //如果是无向图,还要在和主对角线对称处添加值

    if (!directed)

    {

    g[w][v] = new Edge<Weight>(w, v, weight);

    }

    m++;

    }

       

       

    //hasEdge()判断顶点v和顶点w之间是否有边

    //hasEdge()的时间复杂度:O(1)

    bool hasEdge(int v, int w)

    {

    assert(v >= 0 && v < n);

    assert(w >= 0 && w < n);

    return g[v][w] != NULL;

    }

       

       

    void show()

    {

       

    for (int i = 0; i < n; i++)

    {

    for (int j = 0; j < n; j++)

    {

    if (g[i][j])

    {

    cout << g[i][j]->wt() << " ";

    }

    else

    {

    cout << "NULL ";

    }

    }

    cout << endl;

    }

    }

       

       

    //邻边迭代器(相邻,即 adjacent

    class adjIterator

    {

    private:

       

    DenseGraph &G; //图引用,即要迭代的图

    int v; //顶点v

    int index; //相邻顶点的索引

       

    public:

       

    adjIterator(DenseGraph &graph, int v) : G(graph)

    {

    this->v = v;

    this->index = -1;

    }

       

       

    //要迭代的第一个元素

    Edge<Weight> *begin()

    {

    //找第一个权值不为NULL的元素,即为要迭代的第一个元素

    index = -1;

    return next();

    }

       

       

    //要迭代的下一个元素

    Edge<Weight> *next()

    {

    for (index += 1; index < G.V(); index++)

    {

    if (G.g[v][index])

    {

    return index;

    }

    }

       

    return NULL;

    }

       

       

    //判断迭代是否终止

    bool end()

    {

    return index >= G.V();

    }

    };

    };

       

       

    #endif

       

       

       

    ReadGraph.h:

       

    #ifndef READGRAPH_H

    #define READGRAPH_H

       

    #include <iostream>

    #include <string>

    #include <fstream>

    #include <sstream>

    #include <cassert>

    using namespace std;

       

       

       

    //从文件中读取图的测试用例

    template <typename Graph, typename Weight>

    class ReadGraph

    {

       

    public:

    ReadGraph(Graph &graph, const string &filename)

    {

       

    ifstream file(filename);

    string line; //一行一行的读取

    int V, E;

       

    assert(file.is_open());

       

    //读取file中的第一行到line

    assert(getline(file, line));

    //将字符串line放在stringstream

    stringstream ss(line);

    //通过stringstream解析出整型变量:顶点数和边数

    ss >> V >> E;

       

    //确保文件里的顶点数和图的构造函数中传入的顶点数一致

    assert(V == graph.V());

       

    //读取file中的其它行

    for (int i = 0; i < E; i++)

    {

       

    assert(getline(file, line));

    stringstream ss(line);

       

    int a, b;

    Weight w;

    ss >> a >> b >> w;

    assert(a >= 0 && a < V);

    assert(b >= 0 && b < V);

    graph.addEdge(a, b, w);

    }

    }

    };

       

       

    #endif

       

       

       

    main.cpp:

       

    #include "SparseGraph.h"

    #include "DenseGraph.h"

    #include "ReadGraph.h"

    #include <iostream>

    #include <iomanip>

    using namespace std;

       

       

       

    int main()

    {

       

    string filename = "testG1.txt";

    int V = 8;

       

    //设置精确度,这里保留两位小数

    cout << fixed << setprecision(2);

       

    // Test Weighted Dense Graph

    DenseGraph<double> g1 = DenseGraph<double>(V, false);

    ReadGraph<DenseGraph<double>, double> readGraph1(g1, filename);

    g1.show();

    cout << endl;

       

    // Test Weighted Sparse Graph

    SparseGraph<double> g2 = SparseGraph<double>(V, false);

    ReadGraph<SparseGraph<double>, double> readGraph2(g2, filename);

    g2.show();

    cout << endl;

       

    system("pause");

    return 0;

    }

       

       

    运行一览:

       

       

       

       

    其中,testG1.txt 的内容如下:

       

       

       

    该文件可以分成两个部分:

       

    1)第一行:两个数字分别代表顶点数和边数

       

    2)其它行:每一行的前两个数字表示一条边,第三个数字表示权值

       

       

       

       

       

       

       

       

       

       

    【made by siwuxie095】

  • 相关阅读:
    Linux环境下入侵工具Knark的分析及防范 java程序员
    六个步骤即可防范ARP地址欺骗类病毒 java程序员
    巧用命令行 揪出ARP欺骗病毒母机方法 java程序员
    poj3264Balanced Lineup(线段树RMQ)
    有了1A的把握再去提交
    poj2828Buy Tickets(线段树 单点更新+区间求和+区间第K值)
    poj2513Colored Sticks(无向图判欧拉路、回路+trie树)
    ACM数学(转)
    sdut2381Broken Keyboard
    sdut2383Decode the Strings(循环节)
  • 原文地址:https://www.cnblogs.com/siwuxie095/p/7122276.html
Copyright © 2011-2022 走看看