有權圖


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

return 0;

}

   

   

運行一覽:

   

   

   

   

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

   

   

   

該文件可以分成兩個部分:

   

1)第一行:兩個數字分別代表頂點數和邊數

   

2)其它行:每一行的前兩個數字表示一條邊,第三個數字表示權值

   

   

   

   

   

   

   

   

   

   

【made by siwuxie095】


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM