一、介紹
帶權圖(Weighted Graph):帶權圖就是指圖中的每一條邊都有對應的一個或一組值,通常情況下這個值的是數值。
如:在交通運輸網中,邊上的權值可能表示的是路程,也可能表示
的是運輸費用(顯然二者都是數字)。不過,邊上的權值也有可能
是其它東西,比如說是一個字串,甚至是一個更加複雜的資料包,
裡面集合了更多的資料。
二、表示方法
- 鄰接表
在無權圖中鄰接表只存放了相應頂點,而現在帶權圖既需要存放頂點又需要存放權值,所以在帶權圖中的需要一個Edge的類來表示有權圖的頂點和權值,表示方法如下:
這裡再將權值的型別寫成指標型別
- 鄰接矩陣
在帶權中不再使用bool值,但是這裡為了統一介面,統一將鄰接矩陣的表示寫成一個Edge類
這裡再將權值的型別寫成指標型別
三、帶權圖的實現
首先需要編寫一個Edge的類,用來表示帶全圖
#include <iostream> #include <cassert> using namespace std; // 邊 //使用模板型別Weight template<typename Weight> class Edge{ private: //一條邊對應兩個端點 int 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; } // 給定一個頂點, 返回另一個頂點 //other(int 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(); } };
稀疏圖(鄰接表)的實現
在帶權圖中,我們需要將Edge的類的標頭檔案引入#include <iostream> #include <vector> #include <cassert> #include "Edge.h"
需要將原來的資料型別
int
型別改為:<Edge<Weight>*>
型別,如下:vector<vector<Edge<Weight> *> > g;
using namespace std; // 稀疏圖 - 鄰接表 template<typename Weight> class SparseGraph{ private: int n, m; // 節點數和邊數 bool directed; // 是否為有向圖 vector<vector<Edge<Weight> *> > g; // 圖的具體資料
public: // 建構函式 //傳入節點個數和是否為有向圖 SparseGraph( int n , bool directed){ assert(n >= 0); this->n = n; this->m = 0; // 初始化沒有任何邊 this->directed = directed; // g初始化為n個空的vector, 表示每一個g[i]都為空, 即沒有任和邊 g = vector<vector<Edge<Weight> *> >(n, 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;} // 返回邊的個數 // 向圖中新增一個邊, 權值為weight void addEdge( int v, int w , Weight weight){ assert( v >= 0 && v < n ); assert( w >= 0 && w < n ); // 注意, 由於在鄰接表的情況, 查詢是否有重邊需要遍歷整個連結串列 // 我們的程式允許重邊的出現 //需要開空間,Edge<Weight>型別,包括:兩個節點和權值 g[v].push_back(new Edge<Weight>(v, w, weight)); if( v != w && !directed ) g[w].push_back(new Edge<Weight>(w, v, weight)); m ++; } // 驗證圖中是否有從v到w的邊 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; } }
下面來看看稀疏圖的迭代器
其實和前面一樣, 將原來的int
型別改為Edge<Weight>*
// 鄰邊迭代器, 傳入一個圖和一個頂點, // 迭代在這個圖中和這個頂點向連的所有邊 class adjIterator{ private: SparseGraph &G; // 圖G的引用 int v; int index; public: // 建構函式 //傳入圖和一個節點 adjIterator(SparseGraph &graph, int v): G(graph){ this->v = v; this->index = 0; } //解構函式 ~adjIterator(){} // 返回圖G中與頂點v相連線的第一個邊 Edge<Weight>* begin(){ index = 0; if( G.g[v].size() ) return G.g[v][index]; // 若沒有頂點和v相連線, 則返回NULL return NULL; } // 返回圖G中與頂點v相連線的下一個邊 Edge<Weight>* next(){ index += 1; if( index < G.g[v].size() ) return G.g[v][index]; return NULL; } // 檢視是否已經迭代完了圖G中與頂點v相連線的所有頂點 bool end(){ return index >= G.g[v].size(); } }; };
稠密圖—鄰接矩陣的實現
同樣需要將Edge類的標頭檔案引入#include <iostream> #include <vector> #include <cassert> #include "Edge.h" using namespace std;
編寫一個類
同理,需要將原來的bool
型別改為Edge<Weight> *
// 稠密圖 - 鄰接矩陣 template <typename Weight> class DenseGraph{ private: int n, m; // 節點數和邊數 bool directed; // 是否為有向圖 vector<vector<Edge<Weight> *>> g; // 圖的具體資料 public: // 建構函式 DenseGraph( int n , bool directed){ assert( n >= 0 ); this->n = n; this->m = 0; this->directed = directed; // g初始化為n*n的矩陣, 每一個g[i][j]指向一個邊的資訊, 初始化為NULL g = vector<vector<Edge<Weight> *>>(n, 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;} // 返回邊的個數 // 向圖中新增一個邊, 權值為weight 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( v != w && !directed ){ delete g[w][v]; } m --; } g[v][w] = new Edge<Weight>(v, w, weight); if( v != w && !directed ){ g[w][v] = new Edge<Weight>(w, v, weight); } m ++; } // 驗證圖中是否有從v到w的邊 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; } } }
- 稠密圖迭代器的實現
在迭代器中只需要把原來的bool
型別改為Edge<Weight>*
// 鄰邊迭代器, 傳入一個圖和一個頂點,
// 迭代在這個圖中和這個頂點向連的所有邊
class adjIterator{
private:
DenseGraph &G; // 圖G的引用
int v;
int index;
public:
// 建構函式
adjIterator(DenseGraph &graph, int v): G(graph){
this->v = v;
this->index = -1; // 索引從-1開始, 因為每次遍歷都需要呼叫一次next()
}
~adjIterator(){}
// 返回圖G中與頂點v相連線的第一個邊
Edge<Weight>* begin(){
// 索引從-1開始, 因為每次遍歷都需要呼叫一次next()
index = -1;
return next();
}
// 返回圖G中與頂點v相連線的下一個邊
Edge<Weight>* next(){
// 從當前index開始向後搜尋, 直到找到一個g[v][index]為true
for( index += 1 ; index < G.V() ; index ++ ){
if( G.g[v][index] )
return G.g[v][index];
// 若沒有頂點和v相連線, 則返回NULL
return NULL;
}
}
// 檢視是否已經迭代完了圖G中與頂點v相連線的所有邊
bool end(){
return index >= G.V();
}
};
};
本作品採用《CC 協議》,轉載必須註明作者和本文連結