【数据结构】有向图(1.基于邻接表的C++实现)

  最近按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;
}

运行结果:

《【数据结构】有向图(1.基于邻接表的C++实现)》
 

    原文作者:SadGeminids
    原文地址: https://www.cnblogs.com/SadGeminids/archive/2011/11/02/2233575.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞