有权图

------------------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;

}

  

  

booloperator<(Edge<Weight> &e)

{

return weight < e.wt();

}

  

  

booloperator<=(Edge<Weight> &e)

{

return weight <= e.wt();

}

  

  

booloperator>(Edge<Weight> &e)

{

return weight > e.wt();

}

  

  

booloperator>=(Edge<Weight> &e)

{

return weight >= e.wt();

}

  

  

booloperator==(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 << ":\t";

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

{

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

}

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() <<"\t";

}

else

{

cout <<"NULL\t";

}

}

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");

return0;

}

  

  

运行一览:

  

  

  

  

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

  

  

  

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

  

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

  

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

  

  

  

  

  

  

  

  

  

  

【made by siwuxie095】

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值