1.邻接表
图的邻接表存储方法跟树的孩子链表示法相类似,是一种顺序分配和链式分配相结合的存储结构。如这个表头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中。表结点(Nodetable)存放的是邻接顶点在数组中的索引。对于无向图来说,使用邻接表进行存储也会出现数据冗余,表头结点A所指链表中存在一个指向C的表结点的同时,表头结点C所指链表也会存在一个指向A的表结点。
邻接表是图的一种最主要存储结构,用来描述图上的每一个点。对图的每个顶点建立一个容器(n个顶点建立n个容器),第i个容器中的结点包含顶点Vi的所有邻接顶点。实际上我们常用的邻接矩阵就是一种未离散化每个点的边集的邻接表。
2.邻接矩阵和邻接表的选择
邻接矩阵和邻接表这两种图的存储方式孰优孰劣,需要根据具体情况而视。一般来讲主要取决于边的数目。边的数量越多,邻接矩阵的空间效率越高,因为邻接表的指针开销比较大,而邻接矩阵的边可能只需要一个二进制(bit)就可以表示。对于边数较少的,即边数远远小于顶点数目的平方的图,邻接表往往优越于邻接矩阵,使用邻接矩阵可以获得较高的空间效率。
在时间效率方面,邻接表优于邻接矩阵,因为访问图的某个顶点的所有邻接顶点的操作使用最频繁,如果是邻接表,只需要检查此顶点对应的边链表,就能很快地找到所有与此顶点相邻的全部顶点;而在邻接矩阵中,则必须检查某一行的全部矩阵元素。
3.代码实现:
Graphlink.h
#ifndef GRAPHLINK_H
#define GRAPHLINK_H
#include<stdlib.h>
#include<iostream>
using namespace std;
const int Defaultsize=30;
template <class T,class E>
struct Edge
{
//边结点的定义
int dest;
//边上的权值
E cost;
//下一条边链指针
Edge<T,E> *link;
//构造函数
Edge() {}
//带参数构造函数
Edge(int num,E weight):dest(num),cost(weight),link(NULL) {}
bool operator !=(Edge<T,E> &R) const
{
return dest!=R.dest?true:false;
}
};
template<class T,class E>
struct Vertex
{
//顶点的名字
T data;
//边链表的头指针
Edge<T,E> *adj;
};
template<class T,class E>
class Graphlink
{
template <class V,class W>
friend istream& operator >>(istream & in,Graphlink<V,W> &G);
template <class V,class W>
friend ostream& operator<<(ostream &out,Graphlink<V,W> &G);
public:
/** Default constructor 创建顶点列表,并初始化NULL*/
Graphlink(int sz=Defaultsize):maxVertices(sz),numVertices(0),numEdges(0)
{
Nodetable=new Vertex<T,E>[maxVertices];
if(NULL==Nodetable)
{
cerr<<"存储分配错误"<<endl;
exit(1);
}
for(int i=0; i<maxVertices; i++)
{
Nodetable[i].adj=NULL;
}
}
/** Default destructor */
~Graphlink()
{
for(int i=0; i<maxVertices; i++)
{
Edge<T,E> *p=Nodetable[i].adj;
while(NULL!=p)
{
Nodetable[i].adj=p->link;
delete p;
p=Nodetable[i].adj;
}
}
delete []Nodetable;
}
//获取顶点表中第i个位置的顶点
T getValue(int i)
{
return i>=0&&i<numVertices?Nodetable[i].data:NULL;
}
//获取边上的权值
E getWeight(int v1,int v2)
{
if(-1!=v1&&-1!=v2)
{
Edge<T,E> *p=Nodetable[v1].adj;
while(NULL!=p&&v2!=p->dest)
p=p->link;
if(NULL!=p)
return p->cost;
}
return 0;
}
//插入一个顶点
bool insertVertex(const T &vertex)
{
if(numVertices==maxVertices)
return false;
Nodetable[numVertices].data=vertex;
numVertices++;
return true;
}
//删除一个顶点
bool removeVertex(int v)
{
if(1==numVertices||v<0||v>=numVertices)
return false;
Edge<T,E> *p,*s,*t;
int k;
while(NULL!=Nodetable[v].adj)
{
p=Nodetable[v].adj;
k=p->dest;
s=Nodetable[k].adj;
t=NULL;
//处理边结点
while(NULL!=s&&v!=s->dest)
{
t=s;
s=s->link;
}
if(NULL!=s)
{
if(NULL==t)
Nodetable[k].adj=s->link;
else
t->link=s->link;
delete s;
}
Nodetable[v].adj=p->link;
delete p;
numEdges--;
}
//用最后一个顶点填补被删除的顶点的位置
numVertices--;
Nodetable[v].data=Nodetable[v].data;
p=Nodetable[v].adj=Nodetable[numVertices].adj;
while(NULL!=p)
{
s=Nodetable[p->dest].adj;
while(NULL!=s)
{
if(s->dest==numVertices)
{
s->dest=v;
break;
}
else
{
s=s->link;
}
}
}
return true;
}
//插入一条边
bool insertEdge(int v1,int v2,E weight)
{
if(v1>=0&&v1<numVertices&&v2>=0&&v2<numVertices)
{
Edge<T,E> *q,*p=Nodetable[v1].adj;
while(NULL!=p&&NULL!=v2)
p=p->link;
if(NULL!=p)
{
return false;
}
p=new Edge<T,E>();
q=new Edge<T,E>();
p->dest=v2;
p->cost=weight;
p->link=Nodetable[v1].adj;
Nodetable[v1].adj=p;
q->dest=v1;
q->cost=weight;
q->link=Nodetable[v2].adj;
Nodetable[v2].adj=q;
numEdges++;
return true;
}
return false;
}
//删除一条边
bool removeEdge(int v1,int v2)
{
if(-1==v1&&-1==v2)
{
Edge<T,E> *p=Nodetable[v1].adj,*q=NULL,*s=p;
//在v1的边链表中查找被删的边
while(NULL!=p&&p->dest!=v2)
{
q=p;
p=p->link;
}
if(NULL!=p)
{
//改结点是不是边链表的首结点
if(s==p)
Nodetable[v1].adj=p->link;
else
q->link=p->link;
delete p;
}
else
return false;
p=Nodetable[v2].adj;
q=NULL;
s=p;
while(p->dest!=v1)
{
q=p;
p=p->link;
}
if(p==s)
Nodetable[v2].adj=p->link;
else
q->link=p->link;
delete p;
return true;
}
return false;
}
//获取邻接顶点
int GetFirstNeighbor(int v)
{
if(-1!=v)
{
Edge<T,E> *p=Nodetable[v].adj;
if(NULL!=p)
return p->dest;
}
return -1;
}
//获取顶点v的邻接顶点的下一邻接顶点
int getNextNeighbor(int v,int w)
{
if(-1!=v)
{
Edge<T,E> *p=Nodetable[v].adj;
while(NULL!=p&&w!=p->dest)
{
p=p->link;
}
if(NULL!=p&&NULL!=p->link)
return p->link->dest;
}
return -1;
}
//当前定点数
int NumberOfvertices()
{
return numVertices;
}
//返回当前边数
int NumberOfEdges()
{
return numEdges;
}
private:
//最大的顶点树
int maxVertices;
//当前边数
int numEdges;
//当前顶点树
int numVertices;
//顶点表
Vertex<T,E> *Nodetable;
//给出顶点在图中的位置
int getVertexPos(const T Vertex)
{
for(int i=0; i<numVertices; i++)
if(Nodetable[i].data==Vertex)
return i;
return -1;
}
};
#endif // GRAPHLINK_H
main.cpp
#include <iostream>
#include<Graphlink.h>
using namespace std;
int main()
{
return 0;
}
template <class V,class W>
istream& operator >>(istream & in,Graphlink<V,W> &G)
{
//n是顶点数,m是边数
int n,m;
V e1,e2;
W weight;
cout<<"请输入顶点数、边数:"<<endl;
in>>n>>m;
cout<<"请输入顶点:"<<endl;
for(int i=0; i<n; i++)
{
in>>e1;
G.insertVertex(e1);
}
int i=0,j,k;
cout<<"请输入边的两个顶点以及权值:"<<endl;
while(i<m)
{
in>>e1>>e2>>weight;
j=G.getVertexPos(e1);
k=G.getVertexPos(e2);
if(-1==j&&-1==k)
{
cout<<"边的两端信息有误,请重新输入。"<<endl;
}
else
{
G.insertEdge(j,k,weight);
i++;
}
}
return in;
}
template <class V,class W>
ostream& operator<<(ostream &out,Graphlink<V,W> &G)
{
//输出所有顶点和边的信息
int n,m;
V e1,e2;
W w;
n=G.NumberOfvertices();
m=G.NumberOfEdges();
out<<"一共有顶点数:"<<n<<"个 边:"<<m<<"条"<<endl;
for(int i=0; i<n; i++)
for(int j=i+1; j<n; j++)
{
w=G.getWeight(i,j);
if(w>0&&w<G.maxWeight)
{
e1=G.getValue(i);
e2=G.getValue(j);
out<<"("<<e1<<","<<e2<<","<<w<<")"<<endl;
}
}
return out;
}
所有工程文件,请点击我的GitHub