http://www.cnblogs.com/SadGeminids/archive/2011/11/02/2233575.html
最近按BOSS要求做一个有向图,基于邻接表的实现。大部分代码还是参考网上的,然后自己修改了一些细节。参考原帖地址:http://www.cppblog.com/saha/articles/121696.html
先说优缺点吧:优点是基本功能都有了,而且还有Dijkstra算法求最短路径,使用的模板,适合各种数据;缺点是并不像他所说的那样适合大数据的图,另外就是要求一个顶点的逆邻接表(即所有以该顶点为弧头的边)时比较麻烦。
我做过测试,加入10W条边需要20+ minutes,这是个让人无法忍受的时间。大家可以自己试一下,找找到底是哪里花费了大量时间。
下一个博文分析算法效率低劣的原因,然后过几天会另外发一个基于十字链表的实现方式,而且效率至少提少2个数量级。
声明类头文件:
#ifndef __GRAPH_H__ #define __GRAPH_H__ #include <vector> #define IN #define OUT #define INOUT using namespace std; namespace graphspace { template <typename weight> struct Edge //边 { int nDestVertex; //邻接顶点编号 weight edgeWeight; //边权重 Edge<weight> *pNextEdge; //下一条边 Edge(int d, weight c, Edge<weight> *p = NULL) :nDestVertex(d), edgeWeight(c), pNextEdge(p) {} }; template <typename vertexNametype, typename weight> struct Vertex //顶点 { vertexNametype vertexName; //顶点名 Edge<weight> *pAdjEdges; //邻接边链表 Vertex(vertexNametype x, Edge<weight> *p = NULL) :vertexName(x), pAdjEdges(p) {} }; //adjacency list based graph template <typename vertexNametype, typename weight> class ALGraph { public: explicit ALGraph(); ~ALGraph(); public: //插入结点 bool insertAVertex(IN const vertexNametype vertexName); //插入边 bool insertAEdge(IN const vertexNametype vertexName1, IN const vertexNametype vertexName2, IN const weight edgeWeight); //边是否存在 bool edgeExist(IN const vertexNametype vertexName1, IN const vertexNametype vertexName2); //输出顶点的邻接表 void vertexAdjEdges(IN const vertexNametype vertexName); //删除边 bool removeAEdge(IN const vertexNametype vertexName1, IN const vertexNametype vertexName2, IN const weight edgeWeight); //获取最小权 weight getMinWeight(IN const vertexNametype vertexName1, IN const vertexNametype vertexName2); //获取顶点索引 int getVertexIndex(IN const vertexNametype vertexName); //获取顶点数 int getVertexNumber(); //获取顶点名 vertexNametype getData(IN int index); //迪科斯彻算法,最短路径 int Dijkstra(IN const vertexNametype vertexName1); //输出迪科斯彻 void DijkstraPrint(IN int index, IN int sourceIndex, IN vector<int> vecPreVertex); friend ostream& operator<<(OUT ostream &out, IN const ALGraph<vertexNametype,weight> &graphInstance); public: //获取边权 weight getEdgeWeight(IN const Edge<weight> *pEdge); //将顶点的所有邻接边的权值放入数组或者vector中 void getVertexEdgeWeight(IN const int v1, OUT vector<weight> &DistanceArray); vector< Vertex<vertexNametype, weight> > m_vertexArray; }; #include "ALGraph_realize.h" } #endif
实现类头文件:
#ifndef __GRAPH_REALIZE__H_ #define __GRAPH_REALIZE__H_ template<typename vertexNametype, typename weight> ALGraph<vertexNametype, weight>::ALGraph() { //没做重复顶点处理,因为我需要的数据是从另一个系统所得,已经确保没有重复节点 if (!m_vertexArray.empty()) { m_vertexArray.clear(); } } template<typename vertexNametype, typename weight> ALGraph<vertexNametype, weight>::~ALGraph() { vector< Vertex<vertexNametype, weight> >::iterator iter; for(iter = m_vertexArray.begin(); iter != m_vertexArray.end(); iter++) //删除每个结点的邻接链表 { Edge<weight> *p = iter->pAdjEdges; while(NULL != p) //删除邻接链表 { iter->pAdjEdges = p->pNextEdge; delete p; p = iter->pAdjEdges; } } if (!m_vertexArray.empty()) { m_vertexArray.clear(); } } template<typename vertexNametype, typename weight> bool ALGraph<vertexNametype, weight>::insertAVertex(IN const vertexNametype vertexName) { int v = getVertexIndex(vertexName); if (-1 != v) { cerr << "There vertex "<<vertexName<<" is existed!" << endl; return false; } Vertex<vertexNametype, weight> VertexInstance(vertexName, NULL); m_vertexArray.push_back(VertexInstance); return true; } template<typename vertexNametype, typename weight> bool ALGraph<vertexNametype, weight>::insertAEdge(IN const vertexNametype vertexName1, IN const vertexNametype vertexName2, IN const weight edgeWeight) { int v1 = getVertexIndex(vertexName1); if (-1 == v1) { cerr << "There is no vertex 1" << endl; return false; } int v2 = getVertexIndex(vertexName2); if (-1 == v2) { cerr << "There is no vertex 2" << endl; return false; } Edge<weight> *p = m_vertexArray.at(v1).pAdjEdges; while(p != NULL && p->nDestVertex != v2) //邻接链表是否存在,并且搜索V2是否已经在邻接表中了 { p = p->pNextEdge; } if (NULL == p) //邻接表为空,或没找到顶点V2,则添加一条边 { p = new Edge<weight>(v2, edgeWeight, m_vertexArray.at(v1).pAdjEdges); m_vertexArray.at(v1).pAdjEdges = p; return true; } if (v2 == p->nDestVertex) //若V1,V2之间已有一条边,则加一条新的边到已存在的第一条V1->V2边之后 { Edge<weight> *q = p; p = new Edge<weight>( v2, edgeWeight, q->pNextEdge ); q->pNextEdge = p; return true; } return false; } template<typename vertexNametype, typename weight> bool ALGraph<vertexNametype, weight>::edgeExist(IN const vertexNametype vertexName1, IN const vertexNametype vertexName2) { int v1 = getVertexIndex(vertexName1); if (-1 == v1) { cerr << "There is no vertex 1" << endl; return false; } int v2 = getVertexIndex(vertexName2); if (-1 == v2) { cerr << "There is no vertex 2" << endl; return false; } Edge<weight> *p = m_vertexArray.at(v1).pAdjEdges; while(p != NULL && p->nDestVertex != v2) { p = p->pNextEdge; } if(NULL == p) { cout<<"dont exist"<<endl; return false; } if(v2 == p->nDestVertex) { cout<<"exist"<<endl; cout << vertexName1 << ": "; while(p != NULL && p->nDestVertex == v2) //如果v1->v2之间有几条不同的边,都输出 { cout << "(" << vertexName1 << "," << vertexName2 << "," << p->edgeWeight << ") "; p = p->pNextEdge; } cout << endl; return true; } } template<typename vertexNametype, typename weight> void ALGraph<vertexNametype, weight>::vertexAdjEdges(IN const vertexNametype vertexName) { int v1 = getVertexIndex(vertexName); if( -1 == v1) { cerr<<"There is no vertex: "<<vertexName<<endl; return ; } Edge<weigh> *p = m_vertexArray.at(v1).pAdjEdges; cout << vertexName << ": "; while( p != NULL) { cout<<"(" << vertexName << "," << getData(p->nDestVertex) <<"," << p->edgeWeight <<") "; } cout<<endl; } template<typename vertexNametype, typename weight> bool ALGraph<vertexNametype, weight>::removeAEdge(IN const vertexNametype vertexName1, IN const vertexNametype vertexName2, IN const weight edgeWeight) { int v1 = getVertexIndex(vertexName1); if (-1 == v1) { cerr << "There is no vertex 1" << endl; return false; } int v2 = getVertexIndex(vertexName2); if (-1 == v2) { cerr << "There is no vertex 2" << endl; return false; } Edge<weight> *p = m_vertexArray.at(v1).pAdjEdges; Edge<weight> *q = NULL; while(p != NULL && p->nDestVertex != v2 ) { q = p; p = p->pNextEdge; } if (NULL == p) { cerr << "Edge is not found" << endl; return false; } while( edgeWeight != p->edgeWeight && p->nDestVertex == v2) //搜索相同边 { q = p; p = p->pNextEdge; } if (v2 != p->nDestVertex) { cerr << "Edge is not found" << endl; return false; } if( q == NULL ) //m_vertexArray.at(v1).pAdjEdges所指的值即为所求时 m_vertexArray.at(v1).pAdjEdges = p->pNextEdge; else q->pNextEdge = p->pNextEdge; //这里如果同一条边有好几个相同值,只会删除一次 delete p; return true; } template<typename vertexNametype, typename weight> weight ALGraph<vertexNametype, weight>::getEdgeWeight(IN const Edge<weight> *pEdge) { return pEdge->edgeWeight; } template<typename vertexNametype, typename weight> void ALGraph<vertexNametype, weight>::getVertexEdgeWeight(IN const int v1, OUT vector<weight> &DistanceArray) { Edge<weight> *p = m_vertexArray.at(v1).pAdjEdges; int prevIndex = -1; weight tmp; while(NULL != p) { //需考虑相同边的存在,取最小的一个值 if (prevIndex == p->nDestVertex) { if (tmp > p->edgeWeight) { DistanceArray[prevIndex] = p->edgeWeight; } } else { DistanceArray[p->nDestVertex] = p->edgeWeight; prevIndex = p->nDestVertex; tmp = p->edgeWeight; } p = p->pNextEdge; } } template<typename vertexNametype, typename weight> weight ALGraph<vertexNametype, weight>::getMinWeight(IN const vertexNametype vertexName1, IN const vertexNametype vertexName2) { Edge<weight> *pEdge = NULL; int v1 = getVertexIndex(vertexName1); if (-1 == v1) { cerr << "There is no vertex 1" << endl; return false; } int v2 = getVertexIndex(vertexName2); if (-1 == v2) { cerr << "There is no vertex 2" << endl; return false; } Edge<weight> *p = m_vertexArray.at(v1).pAdjEdges; while (p != NULL && p->nDestVertex != v2) { p = p->pNextEdge; } if (NULL == p) { pEdge = NULL; return weight(0); } weight tmp = getEdgeWeight(p); pEdge = p; while (NULL != p && v2 == p->nDestVertex) //相同边,取权值最小的一条 { if (tmp > getEdgeWeight(p)) { tmp = getEdgeWeight(p); pEdge = p; } p = p->pNextEdge; } return tmp; } template<typename vertexNametype, typename weight> int ALGraph<vertexNametype, weight>::getVertexIndex(IN const vertexNametype vertexName) { for (int i = 0; i < m_vertexArray.size(); i++) { if (vertexName == getData(i)) { return i; } } return -1; } template<typename vertexNametype, typename weight> int ALGraph<vertexNametype, weight>::getVertexNumber() { return m_vertexArray.size(); } template<typename vertexNametype, typename weight> vertexNametype ALGraph<vertexNametype, weight>::getData(IN int index) { return m_vertexArray.at(index).vertexName; } template<typename vertexNametype, typename weight> int ALGraph<vertexNametype, weight>::Dijkstra(IN const vertexNametype vertexName1) { int sourceIndex = getVertexIndex(vertexName1); if (-1 == sourceIndex) { cerr << "There is no vertex " << endl; return false; } int nVertexNo = getVertexNumber(); //the array to record the points have been included, if included the value is true //else is false vector<bool> vecIncludeArray; vecIncludeArray.assign(nVertexNo, false); vecIncludeArray[sourceIndex] = true; //the array to record the distance from vertex1 vector<weight> vecDistanceArray; vecDistanceArray.assign(nVertexNo, weight(INT_MAX)); vecDistanceArray[sourceIndex] = weight(0); //prev array to record the previous vertex vector<int> vecPrevVertex; vecPrevVertex.assign(nVertexNo, sourceIndex); getVertexEdgeWeight(sourceIndex, vecDistanceArray); int vFrom, vTo; while(1) { weight minWeight = weight(INT_MAX); vFrom = sourceIndex; vTo = -1; for (int i = 0; i < nVertexNo; i++) { if (!vecIncludeArray[i] && minWeight > vecDistanceArray[i]) { minWeight = vecDistanceArray[i]; vFrom = i; } } if (weight(INT_MAX) == minWeight) { break; } vecIncludeArray[vFrom] = true; Edge<weight> *p = m_vertexArray[vFrom].pAdjEdges; while (NULL != p) { weight wFT = p->edgeWeight; vTo = p->nDestVertex; if (!vecIncludeArray[vTo] && vecDistanceArray[vTo] > wFT + vecDistanceArray[vFrom]) { vecDistanceArray[vTo] = wFT + vecDistanceArray[vFrom]; vecPrevVertex[vTo] = vFrom; } p = p->pNextEdge; } } //print the shortest route of all vertexes for (int i = 0; i < nVertexNo; i++) { if (weight(INT_MAX) != vecDistanceArray[i]) { cout << getData(sourceIndex) << "->" << getData(i) << ": "; DijkstraPrint(i, sourceIndex, vecPrevVertex); cout << "" << vecDistanceArray[i]; cout << endl; } } return 0; } template<typename vertexNametype, typename weight> void ALGraph<vertexNametype, weight>::DijkstraPrint(IN int index, IN int sourceIndex, IN vector<int> vecPreVertex) { if (sourceIndex != index) { DijkstraPrint(vecPreVertex[index], sourceIndex, vecPreVertex); } cout << getData(index) << ""; } template<typename vertexNametype, typename weight> ostream& operator<<(OUT ostream &out, IN ALGraph<vertexNametype,weight> &graphInstance) { int vertexNo = graphInstance.getVertexNumber(); out << "This graph has " << vertexNo << "vertexes" << endl; for(int i = 0; i < vertexNo; i++) { vertexNametype x1 = graphInstance.getData(i); out << x1 << ": "; Edge<weight> *p = graphInstance.m_vertexArray.at(i).pAdjEdges; while (NULL != p) { out << "(" << x1 << "," << graphInstance.getData(p->nDestVertex) << "," << p->edgeWeight << ") "; p = p->pNextEdge; } out << endl; } return out; } #endif
主程序:
#include <iostream> #include <ctime> #include <string> #include "ALGraph.h" using namespace std; using namespace graphspace; int main() { ALGraph<string, int> g; clock_t start1,finish1; double duration; g.insertAVertex("A"); g.insertAVertex("B"); g.insertAEdge("A", "B", 16); g.insertAEdge("A", "B", 26); g.insertAEdge("A", "B", 36); g.insertAEdge("A", "B", 46); g.insertAEdge("A", "B", 6); g.insertAVertex("C"); g.insertAVertex("D"); g.insertAVertex("E"); g.insertAVertex("F"); cout<<g<<endl<<endl; g.insertAEdge("A", "B", 6); g.insertAEdge("A", "C", 3); g.insertAEdge("B", "C", 2); g.insertAEdge("B", "D", 5); g.insertAEdge("C", "D", 3); g.insertAEdge("C", "E", 4); g.insertAEdge("D", "E", 2); g.insertAEdge("D", "F", 3); g.insertAEdge("E", "F", 5); g.insertAEdge("B", "A", 6); g.insertAEdge("C", "A", 3); g.insertAEdge("C", "B", 2); g.insertAEdge("D", "B", 5); g.insertAEdge("D", "C", 3); g.insertAEdge("E", "C", 4); g.insertAEdge("E", "D", 2); g.insertAEdge("F", "D", 3); g.insertAEdge("F", "E", 5); cout<<g<<endl<<endl; g.Dijkstra("A"); /* char vertex[9]; srand((unsigned)time(0)); string str[10000]; for(int i=0; i<10000; i++) //随机生成1 W个顶点 { for(int j=0; j<8; j++) { vertex[j] = rand()%26 + 97; str[i] += vertex[j]; } g.insertAVertex(str[i]); } cout<<g<<endl<<endl; for(int i=0; i<100000; i++) //随机生成10W条边 { int num1 = rand()%10000; int num2 = rand()%10000; int num3 = rand()%10; g.insertAEdge(str[num1], str[num2], num3); } string lastname = "test"; g.insertAVertex(lastname); for(int i=0; i<5000; i++) //单个顶点关联的边并不多,这里单独加一个顶点,关联5K条边 { int num5 = rand()%10000; int num6 = rand()%10000; g.insertAEdge(lastname, str[num5], num6); } cout<<g<<endl<<endl; string vertex1; string vertex2; cout<<"entry 2 vertem name"<<endl; cin>>vertex1; cin>>vertex2; while(vertex1 != "q") { start1 = clock(); g.edgeExist(vertex1, vertex2); finish1 = clock(); duration = (double)(finish1 - start1) / CLOCKS_PER_SEC; cout<<duration<<" seconds"<<endl; cout<<"entry 2 vertem name"<<endl; cin>>vertex1; cin>>vertex2; } */ system("pause"); return 0; }
运行结果: