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

   

   

   

   

   

   

   

   

   

有权图

   

   

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

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

一个数字

   

   

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

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

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

里面集合了更多的数据

   

   

   

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

   

带权 图 python 绘制 把权重画出来 带权图的权值怎么来的_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] 处写上相应的权值

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

   

   

   

(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(如果是有向图,就默认从顶点a指向顶点b)
//边上的权值
   
public:
   
intint b, Weight weight)
 {
this->a = a;
this->b = b;
this->weight = weight;
 }
   
   
//默认构造函数
 Edge(){}
   
   
 ~Edge(){}
   
   
intreturn a; }
   
   
intreturn b; }
   
   
return weight; }
   
   
//知道边的一个顶点x,返回另一个顶点
intint x)
 {
assert(x == a || x == b);
return x == a ? b : a;
 }
   
   
//友元函数重载
friendoperator<<(ostream &os, const Edge &e)
 {
"-"": " << 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 分别表示顶点数和边数
bool//directed表示是有向图还是无向图
//g[i]里存储的就是和顶点i相邻的所有边指针
   
public:
   
intbool directed)
 {
this->n = n;
this->m = 0;
this->directed = directed;
//g[i]初始化为空的vector
forint0; i < n; i++)
 {
 g.push_back(vector<Edge<Weight> *>());
 }
 }
   
   
 ~SparseGraph()
 {
   
forint0; i < n; i++)
 {
forint0; j < g[i].size(); j++)
 {
delete g[i][j];
 } 
 } 
 }
   
   
intreturn n; }
intreturn m; }
   
   
voidintint w, Weight weight)
 {
assert(v >= 0 && v < n);
assert(w >= 0 && w < n);
   
new Edge<Weight>(v, w, weight));
//(1)顶点v不等于顶点w,即不是自环边
//(2)且不是有向图,即是无向图
if (v != w && !directed)
 {
new Edge<Weight>(w, v, weight));
 }
 
 m++;
 }
   
   
//hasEdge()判断顶点v和顶点w之间是否有边
//hasEdge()的时间复杂度:O(n)
boolintint w)
 {
assert(v >= 0 && v < n);
assert(w >= 0 && w < n);
   
forint0; i < g[v].size(); i++)
 {
if (g[v][i]->other(v) == w)
 {
return true;
 } 
 }
 
return false;
 }
   
   
void show()
 {
   
forint0; i < n; i++)
 {
"vertex "":\t";
forint0; j < g[i].size(); j++)
 {
"{to:"",wt:""}\t";
 } 
 cout << endl;
 }
 }
   
   
   
//邻边迭代器(相邻,即 adjacent)
//
//使用迭代器可以隐藏迭代的过程,按照一定的
//顺序访问一个容器中的所有元素
class adjIterator
 {
private:
   
//图的引用,即要迭代的图
int//顶点v
int//相邻顶点的索引
   
public:
   
int v) : G(graph)
 {
this->v = v;
this->index = 0;
 }
   
   
//要迭代的第一个元素
 Edge<Weight> *begin()
 {
//因为有可能多次调用begin(),
//所以显式的将index设置为0
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 分别表示顶点数和边数
bool//directed表示是有向图还是无向图
//二维矩阵,存储边指针
   
public:
   
intbool directed)
 {
this->n = n;
this->m = 0;
this->directed = directed;
//二维矩阵:n行n列,全部初始化为NULL
forint0; i < n; i++)
 {
 g.push_back(vector<Edge<Weight> *>(n, NULL));
 }
 }
   
   
 ~DenseGraph()
 {
forint0; i < n; i++)
 {
forint0; j < n; j++)
 {
if (g[i][j] != NULL)
 {
delete g[i][j];
 } 
 } 
 } 
 }
   
   
intreturn n; }
intreturn m; }
   
   
//在顶点v和顶点w之间建立一条边
voidintint 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--;
 }
   
new Edge<Weight>(v, w, weight);
   
//如果是无向图,还要在和主对角线对称处添加值
if (!directed)
 {
new Edge<Weight>(w, v, weight);
 }
 
 m++;
 }
   
   
//hasEdge()判断顶点v和顶点w之间是否有边
//hasEdge()的时间复杂度:O(1)
boolintint w)
 {
assert(v >= 0 && v < n);
assert(w >= 0 && w < n);
return g[v][w] != NULL;
 }
   
   
void show()
 {
   
forint0; i < n; i++)
 {
forint0; j < n; j++)
 {
if (g[i][j])
 {
"\t";
 } 
else
 {
"NULL\t";
 } 
 } 
 cout << endl;
 }
 }
   
   
//邻边迭代器(相邻,即 adjacent)
class adjIterator
 {
private:
   
//图引用,即要迭代的图
int//顶点v
int//相邻顶点的索引
   
public:
   
int v) : G(graph)
 {
this->v = v;
this->index = -1;
 }
   
   
//要迭代的第一个元素
 Edge<Weight> *begin()
 {
//找第一个权值不为NULL的元素,即为要迭代的第一个元素
1;
return next();
 }
   
   
//要迭代的下一个元素
 Edge<Weight> *next()
 {
for1; 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:
const string &filename)
 {
   
 ifstream file(filename);
//一行一行的读取
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中的其它行
forint0; 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() 
{
   
"testG1.txt";
int8;
   
//设置精确度,这里保留两位小数
2);
   
// Test Weighted Dense Graph
double> g1 = DenseGraph<double>(V, false);
double>, double> readGraph1(g1, filename);
 g1.show();
 cout << endl;
   
// Test Weighted Sparse Graph
double> g2 = SparseGraph<double>(V, false);
double>, double> readGraph2(g2, filename);
 g2.show();
 cout << endl;
   
"pause");
return0;
}

   

   

运行一览:

   

带权 图 python 绘制 把权重画出来 带权图的权值怎么来的_Graph_02

   

   

   

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

   

带权 图 python 绘制 把权重画出来 带权图的权值怎么来的_权值_03

   

   

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

   

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

   

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