有向图(3.基于十字链表的c++实现)

  很抱歉,换电脑后,原文件找不到了。现在的文件已经被我改得面目全非了。所以只好从新浪博客拷贝过来,格式惨不忍睹,让我蛋疼。但我实在是懒得去调整,将就一下吧。

    这次发图的另一种实现,基于十字链表的c++实现。

    就像上一章说的,十字链表是综合了邻接表和逆邻接表的构成,所以,代码需要修改的地方并不是很多。重要的是,修改算法的实现以提高效率。

 

    代码中有两种搜索顶点的实现方式。一种是最原始的,另一种是排序后使用2分搜索。数据量巨大的时候,使用后一种能节省大量的时间。

 

    整个过程中有几个比较需要注意的地方:

    1.析构函数中,顶点释放时,开始错误地先删邻接表,后删逆邻接表。程序运行完毕,调用到析构函数的时候崩掉,才反应过来其实邻接表跟逆邻接表都是指向同样地节点。删除一个,另一个只需要置NULL就行了。一定要记住置NULL,不然悬浮指针可能也会引起问题。

 

    2.2分搜索算法的实现。以前对于递归的实现很不在意,没有考虑太多的内部实现。后来看书才知道,尾部递归是个很差的习惯。所谓“尾部递归”,即在递归函数的最后又调用了自己。每一次递归产生的一些局部变量,开辟的空间会在本轮递归后释放。而如果尾部有递归调用,则会延迟释放的时间,造成大量空间的浪费。仔细思考,在尾部递归调用语句后面,函数不需要再处理什么,而尾部递归所需要的只是一些参数。这样,用递归和循环的思想,可以修改递归的实现方式,消除掉尾部递归,甚至可以消除掉递归,完全使用循环实现。

    比如,代码中原始的2分搜索实现代码为:

template<typename vertexNameType, typename weight>

int OLGraph<vertexNameType, weight>::binarySearchIndex(IN const vertexNameType vertexName, int beginPosition, int size)
{
if ( size == 1)
{
if( m_vertexArray[beginPosition].vertexName == vertexName )
return beginPosition;
else
return -1;
}
if( vertexName == m_vertexArray[beginPosition + size/2 - 1].vertexName )
return (beginPosition + size/2 - 1);
else if( vertexName < m_vertexArray[beginPosition + size/2 - 1].vertexName )
return binarySearchIndex(vertexName, beginPosition, size/2);
else
return binarySearchIndex(vertexName, beginPosition + size/2, size - size/2 );

}

 

    暂且不说这个效率比循环慢了很多,就复杂程度而言,也让人蛋疼。每一次分段的位置,必须要很仔细的检查,不然一不小心就会造成烦人的后果。

    如果采用循环,消除掉递归,实现代码如下:

template<typename vertexNameType, typename weight>
int OLGraph<vertexNameType, weight>::binarySearchIndex(IN const vertexNameType vertexName, int beginPosition, int size)
{
int left = beginPosition;
int right = size - 1;
while(left <= right)
{
int middle = (left + right)/2;
if (vertexName == m_vertexArray[middle].vertexName)
return middle;
if (vertexName > m_vertexArray[middle].vertexName)
left = middle + 1;
else
right = middle - 1;
}
return -1;
}

 

    用循环实现,效率大概提升一倍。

 

    3.指针。十字链表让人很烦躁的一个地方是弧头指针和弧尾指针很容易混淆。有时候一不注意,可能就会写反。尤其是插入边和删除边的时候,涉及到大量指针的做操,很容易出错。推荐先画个图,看着图写指针会清晰容易得多。

 

    关于图的遍历,就留到后面,在讨论深度和广度遍历的时候再写出来。下一章写最短路径和dijkstra算法。

    

    十字链表(OLGraph)代码如下:

#ifndef __GRAPH_H__
#define __GRAPH_H__
#include <vector>
#include <iomanip>
#define IN
#define OUT
#define INOUT
using namespace std;
namespace graphspace
{
template <typename weight>
struct Edge //
{
int tailvex; //弧尾顶点位置
int headvex; //弧头顶点位置
Edge<weight> *hlink; //弧头相同的弧链表
Edge<weight> *tlink; //弧尾相同的弧链表
weight edgeWeight; //弧权重
Edge(int a, int b, weight c, Edge<weight> *p = NULL, Edge<weight> *q = NULL)
:tailvex(a), headvex(b), edgeWeight(c), hlink(p), tlink(q)
{}
};
template <typename vertexNameType, typename weight>
struct Vertex //顶点
{
vertexNameType vertexName; //顶点名
Edge<weight> *firstin; //指向第一条入弧(即指向逆邻接表)
Edge<weight> *firstout; //指向第一条出弧(即指向邻接表)
Vertex(vertexNameType x, Edge<weight> *p = NULL, Edge<weight> *q = NULL)
:vertexName(x), firstin(p), firstout(q)
{}
};
template <typename vertexNameType, typename weight>
class OLGraph
{
public:
explicit OLGraph();
~OLGraph();
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);
//输出单个节点入度信息,弧头相同的链表(逆邻接表)
void vertexRAdjEdges(IN const vertexNameType vertexName);
//删除边
bool removeAEdge(IN const vertexNameType vertexName1, IN const vertexNameType vertexName2, IN const weight edgeWeight);
//获取边权
weight getEdgeWeight(IN const Edge<weight> *pEdge);
//将顶点的所有邻接边的权值放入数组或者vector中
void getVertexEdgeWeight(IN const int v1, OUT vector<weight> &DistanceArray);

//获取最小权
weight getMinWeight(IN const vertexNameType vertexName1, IN const vertexNameType vertexName2);
//获取顶点索引
int getVertexIndex(IN const vertexNameType vertexName);
//获取顶点索引(2分搜索)
int binarySearchIndex(IN const vertexNameType vertexName, int beginPosition, int size);
//获取顶点名
vertexNameType getData(IN int index);
//获取顶点数
int getVertexNumber();
//迪科斯彻算法,最短路径
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 OLGraph<vertexNameType,weight> &graphInstance);
public:
vector< Vertex<vertexNameType, weight> > m_vertexArray;

};

#include "OLGraph_realize.h"
}
#endif

 

实现类头文件:

#ifndef __GRAPH_REALIZE__H_
#define __GRAPH_REALIZE__H_
#include <ctime>
template<typename vertexNameType, typename weight>
OLGraph<vertexNameType, weight>::OLGraph()
{
if( !m_vertexArray.empty() )
m_vertexArray.clear();
}

template<typename vertexNameType, typename weight>
OLGraph<vertexNameType, weight>::~OLGraph()
{
vector< Vertex<vertexNameType, weight> >::iterator iter;
for( iter = m_vertexArray.begin(); iter != m_vertexArray.end(); iter++ )
{
//Error:删除两次节点
//邻接表跟逆邻接表指向的是同样节点,删除其中之一即可,但是另一个指针一定不要忘记置NULL
Edge<weight> *p = iter->firstin;
while( NULL != p ) //删除逆邻接表
{
iter->firstin = p->hlink;
delete p;
p = iter->firstin;
}
iter->firstout = NULL;

}
if( !m_vertexArray.empty())
m_vertexArray.clear();
}

template<typename vertexNameType, typename weight>
bool OLGraph<vertexNameType, weight>::insertAVertex(IN const vertexNameType vertexName)
{
//任然不做节点重复处理,因为数据来自另一个系统,已经保证不重复
Vertex<vertexNameType, weight> VertexInstance(vertexName, NULL);
m_vertexArray.push_back(VertexInstance);
return true;
}

template<typename vertexNameType, typename weight>
bool OLGraph<vertexNameType, weight>::insertAEdge(IN const vertexNameType vertexName1,
IN const vertexNameType vertexName2, IN const weight edgeWeight)
{
int v1 = -1;
int v2 = -1;
int size = m_vertexArray.size();
//2分搜索定位V1,V2在容器中的位置
v1 = binarySearchIndex(vertexName1, 0, size);
v2 = binarySearchIndex(vertexName2, 0, size);

if (-1 == v1)
{
cerr << "There is no vertex 1" << endl;
return false;
}
if (-1 == v2)
{
cerr << "There is no vertex 2" << endl;
return false;
}
Edge<weight> *p = m_vertexArray.at(v1).firstout;

while( p != NULL && p->headvex != v2 )
{
p = p->tlink;
}
if( NULL == p ) //不存在边v1->v2,将新边插入到链表头
{
p = new Edge<weight>(v1, v2, edgeWeight, m_vertexArray.at(v2).firstin, m_vertexArray.at(v1).firstout);
m_vertexArray.at(v1).firstout = p;
m_vertexArray.at(v2).firstin = p;
return true;
}
if( v2 == p->headvex) //已存在v1->v2的边
{
Edge<weight> *q = p;
Edge<weight> *t = m_vertexArray.at(v2).firstin;
while( t != NULL && t->tailvex != v1) //找到边v1->v2在逆邻接表中的位置
{
t = t->hlink;
}
p = new Edge<weight>(v1, v2, edgeWeight, t->hlink, q->tlink); //将新边加在已存在的第一条v1->v2后面
q->tlink = p;
t->hlink = p;
return true;
}
return false;
}
template<typename vertexNameType, typename weight>
int OLGraph<vertexNameType, weight>::binarySearchIndex(IN const vertexNameType vertexName, int beginPosition, int size)
{

int left = beginPosition;
int right = size - 1;
while(left <= right)
{
int middle = (left + right)/2;
if (vertexName == m_vertexArray[middle].vertexName)
return middle;
if (vertexName > m_vertexArray[middle].vertexName)
left = middle + 1;
else
right = middle - 1;
}
return -1;
}

template<typename vertexNameType, typename weight>
bool OLGraph<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).firstout;
while(p != NULL && p->headvex != v2)
{
p = p->tlink;
}
if(NULL == p)
{
cout<<"dont exist"<<endl;
return false;
}
if(v2 == p->headvex) //存在,则输出所有的v1->v2边
{
cout<<"exist"<<endl;
cout << vertexName1 << ": ";
while(p != NULL && p->headvex == v2)
{
cout << "(" << vertexName1 << "," << vertexName2 << "," << p->edgeWeight << ") ";
p = p->tlink;
}
cout << endl;
return true;
}
}

template<typename vertexNameType, typename weight>
void OLGraph<vertexNameType, weight>::vertexAdjEdges(IN const vertexNameType vertexName)
{
int v1 = getVertexIndex(vertexName);
if( -1 == v1)
{
cerr<<"There is no vertex: "<<vertexName<<endl;
return ;
}
Edge<weight> *p = m_vertexArray.at(v1).firstout;
cout << vertexName << ": ";
while( p != NULL) //顺着邻接表输出边信息
{
cout<<"(" << vertexName << "," << getData(p->headvex) <<"," << p->edgeWeight <<") ";
p = p->tlink;
}
cout<<endl;
}
template<typename vertexNameType, typename weight>
void OLGraph<vertexNameType, weight>::vertexRAdjEdges(IN const vertexNameType vertexName)
{
int v1 = getVertexIndex(vertexName);
if( -1 == v1)
{
cerr<<"There is no vertex: "<<vertexName<<endl;
return ;
}
Edge<weight> *p = m_vertexArray.at(v1).firstin;
cout << vertexName << ": ";
while( p != NULL) //顺着逆邻接表输出信息
{
cout<<"(" << getData(p->tailvex) << "," << vertexName <<"," << p->edgeWeight <<") ";
p = p->hlink;
}
cout<<endl;
}
template<typename vertexNameType, typename weight>
bool OLGraph<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).firstout;
Edge<weight> *q = NULL;
while( p != NULL && p->headvex != v2 )
{
q = p;
p = p->tlink;
}
if (NULL == p)
{
cerr << "Edge is not found" << endl;
return false;
}
while( edgeWeight != p->edgeWeight && p->headvex == v2 )
{
q = p;
p = p->tlink;
}
if (v2 != p->headvex)
{
cerr << "Edge is not found" << endl;
return false;
}

Edge<weight> *t = m_vertexArray.at(v2).firstin;
Edge<weight> *s = NULL;
while ( t != p ) //边若存在,那么一定是p指向的边,所以t的搜索条件与p相比即可
{
s = t;
t = t->hlink;
}

//修改v1的邻接表
if( q == NULL ) //邻接表第一个即所要删除的边
m_vertexArray.at(v1).firstout = p->tlink;
else
q->tlink = p->tlink;
//修改v2的逆邻接表
if( s == NULL ) //逆邻接表第一个即所要删除的边
m_vertexArray.at(v2).firstin = t->hlink;
else
s->hlink = t->hlink;
delete p;

return true;
}
template<typename vertexNameType, typename weight>
weight OLGraph<vertexNameType, weight>::getEdgeWeight(IN const Edge<weight> *pEdge)
{
return pEdge->edgeWeight;
}

template<typename vertexNameType, typename weight>
void OLGraph<vertexNameType, weight>::getVertexEdgeWeight(IN const int v1, OUT vector<weight> &DistanceArray)
{
Edge<weight> *p = m_vertexArray.at(v1).firstout;
int prevIndex = -1;
weight tmp;
while(NULL != p)
{
if (prevIndex == p->headvex) //相同边找权值最小的一个
{
if (tmp > p->edgeWeight)
{
DistanceArray[prevIndex] = p->edgeWeight;
}
}
else
{
DistanceArray[p->headvex] = p->edgeWeight;
prevIndex = p->headvex;
tmp = p->edgeWeight;
}

p = p->tlink;
}
}

template<typename vertexNameType, typename weight>
weight OLGraph<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).firstout;
while (p != NULL && p->headvex != v2)
{
p = p->tlink;
}
if (NULL == p)
{
pEdge = NULL;
return weight(0);
}
weight tmp = getEdgeWeight(p);
pEdge = p;
while (NULL != p && v2 == p->headvex)
{
if (tmp > getEdgeWeight(p))
{
tmp = getEdgeWeight(p);
pEdge = p;
}
p = p->tlink;
}
return tmp;
}

template<typename vertexNameType, typename weight>
int OLGraph<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>
vertexNameType OLGraph<vertexNameType, weight>::getData(IN int index)
{
return m_vertexArray.at(index).vertexName;
}

template<typename vertexNameType, typename weight>
int OLGraph<vertexNameType, weight>::getVertexNumber()
{
return m_vertexArray.size();
}
template<typename vertexNameType, typename weight>
int OLGraph<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();
vector<bool> vecIncludeArray;
vecIncludeArray.assign(nVertexNo, false);
vecIncludeArray[sourceIndex] = true;
vector<weight> vecDistanceArray;
vecDistanceArray.assign(nVertexNo, weight(INT_MAX));
vecDistanceArray[sourceIndex] = weight(0);
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].firstout;
while (NULL != p)
{
weight wFT = p->edgeWeight;
vTo = p->headvex;
if (!vecIncludeArray[vTo] && vecDistanceArray[vTo] > wFT + vecDistanceArray[vFrom])
{
vecDistanceArray[vTo] = wFT + vecDistanceArray[vFrom];
vecPrevVertex[vTo] = vFrom;
}
p = p->tlink;
}

}
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 OLGraph<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 OLGraph<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).firstout;
while (NULL != p)
{
out << "(" << x1 << "," << graphInstance.getData(p->headvex) << "," << p->edgeWeight << ") ";
p = p->tlink;
}
out << endl;
}
return out;
}
#endif

 

主程序:

#include <iostream>
#include <ctime>
#include <string>
#include <algorithm>
#include "OLGraph.h"
using namespace std;
using namespace graphspace;
int main()
{
OLGraph<string, int> g;
clock_t start1,finish1,time1;
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;


cout<<g<<endl<<endl;
g.Dijkstra("A");
g.vertexRAdjEdges("B");
string vertex1;
string vertex2;
int controlNum;
int value;
cout<<"查询边请按 1,查询顶点邻接表请按 2,查询顶尖逆邻接表请按3 ,删除边请按4,退出请按 0:"<<endl;
cin>>controlNum;
while( controlNum != 0 )
{
start1 = clock();
switch ( controlNum ){
case 1:
cout<<"entry 2 vertex name"<<endl;
cin>>vertex1>>vertex2;
g.edgeExist(vertex1, vertex2);
cout<<endl;
break;
case 2:
cout<<"entry 1 vertex name"<<endl;
cin>>vertex1;
g.vertexAdjEdges(vertex1);
cout<<endl;
break;
case 3:
cout<<"entry 1 vertex name"<<endl;
cin>>vertex1;
g.vertexRAdjEdges(vertex1);
cout<<endl;
break;
case 4:
cout<<"entry 2 vertex name"<<endl;
cin>>vertex1>>vertex2;
cin>>value;
g.removeAEdge(vertex1, vertex2, value);
cout<<g<<endl<<endl;
break;
default:
break;
}

cout<<"查询边请按 1,查询顶点邻接表请按 2,查询顶尖逆邻接表请按3 ,删除边请按4,退出请按 0:"<<endl;
cin>>controlNum;
finish1 = clock();
duration = (double)(finish1 - start1) / CLOCKS_PER_SEC;
cout<<duration<<" seconds"<<endl;
}

system("pause");
return 0;
}




转载于:https://www.cnblogs.com/SadGeminids/archive/2011/12/23/2299207.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值