赞
踩
1.适合存稠密图
2.邻接矩阵判断两个顶点的连接关系,并取到权值时间复杂度为o(1)
1.如果顶点比较多,边比较少时,矩阵中存储了大量的0成为系数矩阵,比较浪费空间。
2.并且要求两个节点之间的路径不是很好求。
namespace matrix { template<class V, class W,W MAX_W=INT_MAX, bool Direction=false> class Graph { public: //图的创建 Graph(const V* a, size_t n) { _vertexs.reserve(n); for (size_t i = 0; i < n; i++) { _vertexs.push_back(a[i]); _indexMap[a[i]] = i; } _matrix.resize(n); for (size_t i = 0; i < _matrix.size(); i++) { _matrix[i].resize(n, MAX_W); } } size_t GetVertexIndex(const V& v) { auto it = _indexMap.find(v); if (it != _indexMap.end()) { return it->second; } else { throw invalid_argument("顶点不存在"); return -1; } } void AddEdge(const V& src, const V& dst, const W& w) { size_t srci = GetVertexIndex(src); size_t desti = GetVertexIndex(dst); _matrix[srci][desti] = w; if (Direction == false) { _matrix[desti][srci] = w; } } void Print() { //顶点 int n = _vertexs.size(); for (size_t i = 0; i < n; i++) { cout << i << "->" << _vertexs[i] << endl; } cout << endl; //矩阵 cout << " "; for (size_t i = 0; i < _vertexs.size(); i++) { printf("%4d", i); } cout << endl; for (size_t i = 0; i < _matrix.size(); i++) { cout << i << " ";//纵坐标 for (size_t j = 0; j < _matrix[i].size(); j++) { if (_matrix[i][j] == MAX_W) { //cout << "*"<<" "; printf("%4c", '*'); } else { //cout << _matrix[i][j] << " "; printf("%4d", _matrix[i][j]); } } cout << endl; } } //void BFS(const V& src) //{ // size_t srci = GetVertexIndex(src); // int n = _matrix.size(); // queue<int> q; // q.push(srci); // vector<bool> vis(_vertexs.size(),false); // vis[srci] = true; // while (!q.empty()) // { // int front = q.front(); // q.pop(); // cout << front << "->" << _vertexs[front] << " "; // //邻接顶点入队列 // for (size_t i = 0; i < n; i++) // { // if (_matrix[front][i] != MAX_W&&!vis[i]) // { // q.push(i); // vis[i] = true; // } // } // } //} void BFS2(const V& src) { size_t srci = GetVertexIndex(src); int n = _matrix.size(); queue<int> q; q.push(srci); vector<bool> vis(_vertexs.size(), false); vis[srci] = true; int count = 0; while (!q.empty()) { int m = q.size(); for (size_t i = 0; i <m; i++) { int front = q.front(); q.pop(); cout << count << "层朋友:" << _vertexs[front] << " "; //邻接顶点入队列 for (size_t i = 0; i < n; i++) { if (_matrix[front][i] != MAX_W && !vis[i]) { q.push(i); vis[i] = true; } } } count++; cout << endl; } } void _DFS(size_t srci, vector<bool>& vis) { cout << srci << ":" << _vertexs[srci] << endl; vis[srci] = true; //找一个srci没有访问过的点,去深度遍历 for (size_t i = 0; i < _vertexs.size(); i++) { if (_matrix[srci][i] != MAX_W && vis[i] == false) { _DFS(i, vis); } } } void DFS(const V& src) { size_t srci = GetVertexIndex(src); vector<bool> vis(_vertexs.size(), false); _DFS(srci, vis); //如果不连通,还有检查vis哪里有false,循环遍历一遍 for (size_t i = 0; i < _vertexs.size(); i++) { if (!vis[i]) _DFS(i,vis); } } private: vector<V> _vertexs; //顶点 map<V, int> _indexMap; //顶点映射下标 vector<vector<W>> _matrix;//邻接矩阵 }; void Test_Graph1() { Graph<char, int, INT_MAX, true> g("0123", 4); g.AddEdge('0', '1', 1); g.AddEdge('0', '3', 4); g.AddEdge('1', '3', 2); g.AddEdge('1', '2', 9); g.AddEdge('2', '3', 8); g.AddEdge('2', '1', 5); g.AddEdge('2', '0', 3); g.AddEdge('3', '2', 6); //g.Print(); g.BFS2('0'); } void Test_Graph2() { string a[] = { "张三", "李四", "王五", "赵六","周七","勾八"}; Graph<string, int> g1(a, 6); g1.AddEdge("张三", "李四", 100); g1.AddEdge("张三", "王五", 200); g1.AddEdge("王五", "赵六", 30); g1.AddEdge("王五", "李四", 80); g1.AddEdge("李四", "勾八", 50); //g1.BFS2("张三"); g1.DFS("王五"); } }
用vector保存所有顶点,使用链表挂在每一个顶点下面,形成一个类似哈希桶的结构。
1.适合存稀疏图
2.适合查找一个顶点连接出去的边
不适合确定两个顶点是否相连及权值
namespace link_table { template<class W> struct Edge { int _dsti; //int _srci; W _w; Edge<W>* _next=nullptr; Edge(const int dsti,const W& w) :_dsti(dsti) ,_w(w) ,_next(nullptr) {} }; template<class V,class W,bool Direction=false> class Graph { typedef Edge<W> Edge; public: Graph(const V* a, size_t n) { _vertexs.reserve(n); for (size_t i = 0; i < n; i++) { _vertexs.push_back(a[i]); _indexMap[a[i]] = i; } _tables.resize(n,nullptr); } size_t GetVertexIndex(const V& v) { auto it = _indexMap.find(v); if (it != _indexMap.end()) { return it->second; } else { throw invalid_argument("顶点不存在"); return -1; } } void AddEdge(const V& src, const V& dst, const W& w) { size_t srci = GetVertexIndex(src); size_t dsti = GetVertexIndex(dst); Edge* eg=new Edge(dsti,w) ;//构造一条边 eg->_next = _tables[srci]; _tables[srci] = eg; if (Direction == false) { Edge* eg = new Edge(srci, w); eg->_next = _tables[dsti]; _tables[dsti] = eg; } } void Print()const { for (size_t i = 0; i < _vertexs.size(); i++) { cout << i << "->" << _vertexs[i] << endl; } cout << endl; for (size_t i = 0; i < _tables.size(); i++) { cout << _vertexs[i] << " " << i << "->"; Edge* cur = _tables[i]; while (cur) { cout << _vertexs[cur->_dsti] << "[" << cur->_dsti << "]"<<cur->_w<<"->"; cur = cur->_next; } cout << " nullptr" << endl; } } private: vector<V> _vertexs;//顶点集合 map<V, int> _indexMap;//点和下标映射关系 vector<Edge*> _tables;//边的集合 }; void test_link_tables() { string a[] = { "张三", "李四", "王五", "赵六"}; Graph<string, int> g1(a, 4); g1.AddEdge("张三", "李四", 100); g1.AddEdge("张三", "王五", 200); g1.AddEdge("王五", "赵六", 30); g1.Print(); } }
完全图:在有n个顶点的无向图中,若有n *(n-1)/2条边,即任意两个顶点之间有且仅有一条边,则称此图为无向完全图,比如上图G1;在n个顶点的有向图中,若有n *(n-1)条边,即任意两个顶点之间有且仅有方向相反的边,则称此图为有向完全图。
顶点的度:顶点v的度是指与它相关联的边的条数,记作deg(v)。在有向图中,顶点的度等于该顶点的入度与出度之和,其中顶点v的入度是以v为终点的有向边的条数,记作indev(v);顶点v的出度是以v为起始点的有向边的条数,记作outdev(v)。因此:dev(v) = indev(v) + outdev(v)。注意:对于无向图,顶点的度等于该顶点的入度和出度,即dev(v) = indev(v) = outdev(v)。
连通图:在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。如果图中任意一对顶点都是连通的,则称此图为连通图。
强连通图:在有向图中,若在每一对顶点vi和vj之间都存在一条从vi到vj的路径,也存在一条从vj到vi的路径,则称此图是强连通图。生成树:一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点和n-1条边
连通图中的每一棵生成树,都是原图的一个极大无环子图,即:从其中删去任何一条边,生成树就不在连通;反之,在其中引入任何一条新边,都会形成一条回路。
若连通图由n个顶点组成,则其生成树必含n个顶点和n-1条边。因此构造最小生成树的准则有三条:
简单来说,该算法是在全局的边中采用贪心算法,先构建一个无边的顶点集合,然后从权值最小的边开始,一条一条选,若两个顶点来自不同的连通分量,则加入到顶点集合中,(使用并查集来保证两个顶点来自不同的连通分量)。
struct Edge { size_t _srci; size_t _dsti; W _w; Edge(size_t srci, size_t dsti, const W& w) :_srci(srci) , _dsti(dsti) , _w(w) {} bool operator>(Edge e2)const { return _w > e2._w; } }; W Kruskal(Self& minTree) { //初始化minTree minTree._vertexs = _vertexs; minTree._indexMap = _indexMap; minTree._matrix.resize(_vertexs.size()); for (size_t i = 0; i < _vertexs.size(); i++) { minTree._matrix[i].resize(_vertexs.size(), MAX_W); } priority_queue<Edge, vector<Edge>, greater<Edge>> minq; int n = _matrix.size(); for (size_t i = 0; i < n; i++) { for (size_t j = 0; j < n; j++) { if (i < j && _matrix[i][j] != MAX_W) { minq.push(Edge(i, j, _matrix[i][j])); } } } //选出n-1条边 UnionFindSet ufs(n); W totalW = W(); int size = 0; while (!minq.empty()) { Edge min = minq.top(); minq.pop(); if (!ufs.IsInSet(min._srci, min._dsti)) { cout << _vertexs[min._srci] << "-" << _vertexs[min._dsti] << ":" << _matrix[min._srci][min._dsti] << endl; minTree._AddEdge(min._srci, min._dsti, min._w); ufs.Union(min._srci, min._dsti); totalW += min._w; size++; } } if (size == n - 1) { return totalW; } else { return W(); } }
这里首先创建一个Edge结构体用来存起始顶点和权值,采用优先级队列辅助每次取出最小的元素(用一个仿函数控制为权值比较的小堆),然后进行n-1次循环,每次把选取的Edge的顶点下标放进并查集中,最终若为最小生成树则返回权值和
从任意一个根节点开始,从这个起点出发选择一条轻量级的边,然后把这两个点归到集合A中,剩下的点在集合B中,再从集合B中选择一条小边再加入到集合A,循环往复,直到选出n-1条不成环的边。
W Prim(Self& minTree, const V& src) { size_t srci = GetVertexIndex(src); //初始化minTree minTree._vertexs = _vertexs; minTree._indexMap = _indexMap; int n = _vertexs.size(); minTree._matrix.resize(_vertexs.size()); for (size_t i = 0; i < _vertexs.size(); i++) { minTree._matrix[i].resize(_vertexs.size(), MAX_W); } unordered_set<int> X; unordered_set<int> Y; X.insert(srci); for (size_t i = 0; i < n; i++) { if (i != srci) { Y.insert(i); } } //从X到Y集合中连最小边 priority_queue<Edge, vector<Edge>, greater<Edge>> minq; for (size_t i = 0; i < n; i++) { if (_matrix[srci][i] != MAX_W) { minq.push(Edge(srci, i, _matrix[srci][i])); } } size_t size = 0; W totalW = W(); while (!minq.empty()) { Edge min = minq.top(); minq.pop(); if (X.count(min._dsti)) { cout << "构成环" << _vertexs[min._srci] << ":" << _vertexs[min._dsti] << endl; continue; } minTree._AddEdge(min._srci, min._dsti,min._w); X.insert(min._dsti); Y.erase(min._dsti); totalW += min._w; size++; if (size == n - 1) break; for (size_t i = 0; i < n; i++) { if (_matrix[min._dsti][i] != MAX_W&&X.count(i)==0) { minq.push(Edge(min._dsti, i, _matrix[min._dsti][i])); } } } if (size == n - 1) return totalW; else return W(); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。