7.2 图的存储结构
7.2.2 邻接表Adjacency List
邻接表是邻接矩阵的改进。当图中的边数很少时,邻接矩阵中会出现大量的零元素,为了存储这些零元素,将耗费大量的存储空间。为此,可以把邻接矩阵的每一行改为一个单链表。
定义
在邻接表中,对图中每个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点vi的边(对有向图是以顶点vi为尾的弧)。每个链表上附设一个表头结点。
在邻接表中,对于图中的每一个顶点用一个结点表示,称为顶点结点(头结点)。顶点结点中保存了该顶点的信息和指向该顶点相应的边(弧)链表的指针。所有的顶点结点用顺序表存储,并假设顶点的序号为数组的下标。
- 无向图的邻接表
对于无向图,把依附于同一个顶点的边链接在同一个单链表中称为边链表,边链表中的每一个结点代表一条边叫作边结点。在边结点中保存着与该边相关联的另一顶点的序号和指向同一链表中下一个边结点的指针。
从无向图的邻接表中可以看到,同一条边在邻接表中出现了两次,这是因为(v,v)与(V,v)虽是同一条边,但在邻接表中,(v,v)对应的边结点在顶点v的边链表中;(v,vi)对应的边结点在顶点v;的边链表中。如果想知道顶点vi的度,只需统计顶点v的边链表中边结点的个数即可。 - 有向图的邻接表
对于有向图,把从同一个顶点发出的弧链接在同一个单链表中称为弧链表,弧链表的每一个结点代表一条弧叫作弧结点,在弧结点中保存着该弧的弧头顶点序号和指向同一链表中下一个弧结点的指针。
在有向图的邻接表中,一条弧在邻接表中只出现一次。如果统计顶点vi的弧链表中的弧结点个数,只能得到该顶点的出度,所以这种链表也称为出弧表;若想要知道该顶点的入度,必须检测其他所有的弧链表,看有多少个弧结点的弧头顶点序号为vi,显然,这是不方便的。
为此,对有向图可以建立逆邻接表。在有向图的逆邻接表中,顶点vi的弧链表中链接的是所有进入该顶点的弧,所以也称为入弧表。统计顶点的弧链表中结点的个数,就能得到该顶点的入度。 - 带权图(网络)的邻接表
如果是带权图,则在边(弧)结点中还要保存该边(或弧)上的权值weight。
- 在邻接表的边(或弧)链表中,各个边(或弧)结点的链入顺序是任意的,可根据边结点输入次序而定。
- 若设图有n个顶点和e条边,则用邻接表表示无向图时,需要n个顶点结点和2e个边结点;用邻接表表示有向图时,若不考虑逆邻接表,只需n个顶点结点和e个边结点。
- 当e很小时,可以节省大量存储空间。此外,把关联于同一个顶点的所有边(或弧)链接在一个单链表中,可以大大方便图的操作。
- 在无向图的邻接表中,顶点vi的度恰为第i个链表中的结点数;
在有向图的邻接表中,第i个链表中的结点数只是顶点v;的出度,要求入度,必须遍历整个邻接表。
有向网邻接表的实现
顶点结点类模板
在顶点结点类模板中定义了两个数据成员:
其一是data域,它存储顶点信息;
其二是firstarc域,它是一个指针,指向从该顶点出发的第一条弧结点。
// 邻接表网顶点结点类
template <class ElemType, class WeightType>
struct AdjListNetWorkVex
{
// 数据成员:
ElemType data; // 数据元素值
AdjListNetworkArc<WeightType> *firstarc;
// 指向邻接链表边结点的指针
// 构造函数:
AdjListNetWorkVex(); // 无参数的构造函数
AdjListNetWorkVex(ElemType val,
AdjListNetworkArc<WeightType> *adj = NULL);
// 有参数的构造函数
};
// 邻接表网顶点结点类的实现部分
template <class ElemType, class WeightType>
AdjListNetWorkVex<ElemType, WeightType>::AdjListNetWorkVex()
// 操作结果:构造一个空顶点结点——无参构造函数
{
firstarc = NULL;
}
template <class ElemType, class WeightType>
AdjListNetWorkVex<ElemType, WeightType>::AdjListNetWorkVex(ElemType val,
AdjListNetworkArc<WeightType> *adj)
// 操作结果:构造数据为val,边为eg的顶点
{
data = val;
firstarc = adj;
}
弧结点类模板
在弧结点类模板中定义了三个数据成员:
其一是adjVex域,它存储弧头顶点的序号;
其二是weight域,它存储这条弧上的权值;
其三是nextarc域,它是一个指针,指向从该顶点出发的下一条弧结点。
// 邻接表网边数据类
template <class WeightType>
struct AdjListNetworkArc
{
// 数据成员:
int adjVex; // 弧头顶点序号
WeightType weight; // 边的权值
AdjListNetworkArc<WeightType> *nextarc; // 下一条边结点的指针
// 构造函数:
AdjListNetworkArc(); // 无参数的构造函数
AdjListNetworkArc(int v, WeightType w, AdjListNetworkArc<WeightType> * next = NULL);
// 构造邻接点为v,权为w的邻接边
};
// 邻接表网边数据类的实现部分
template <class WeightType>
AdjListNetworkArc<WeightType>::AdjListNetworkArc()
// 操作结果:构造一个空邻接表边结点边——无参构造函数
{
adjVex = -1;
}
template <class WeightType>
AdjListNetworkArc<WeightType>::AdjListNetworkArc(int v, WeightType w, AdjListNetworkArc<WeightType> *next)
// 操作结果:构造邻接点序号为v,边的权为w,下一条边结点的指针为next的邻接边
{
adjVex = v;
weight = w;
nextarc = next;
}
有向网邻接表类模板
- 类定义
其中顶点表vexTable是一个顶点数组,每个顶点对应一个数组元素,用以存放顶点信息和指向从该顶点出发的第一个弧结点;
还定义了vexNum、vexMaxNum和arcNum三个数据成员,分别记录图中当前顶点数日、允许的顶点最大数目和弧数;
为了在图的遍历等算法中记录顶点是否访问,在此定义了一个标志数组tag,需要时用以记录顶点的访问状况;
另外数据成员infinity表示无穷大的值。
// 有向网的邻接表类
template <class ElemType, class WeightType>
class AdjListDirNetwork
{
protected:
// 邻接表的数据成员:
int vexNum, vexMaxNum, arcNum; // 顶点数目、允许的顶点最大数目和边数
AdjListNetWorkVex<ElemType, WeightType> *vexTable; // 顶点表
mutable Status *tag; // 标志数组
WeightType infinity; // 无穷大的值
public:
// 抽象数据类型方法声明及重载编译系统默认方法声明:
AdjListDirNetwork(ElemType es[], int vertexNum, int vertexMaxNum = DEFAULT_SIZE,
WeightType infinit = (WeightType)DEFAULT_INFINITY);
// 以数组es[]为顶点数据,顶点个数为vertexNum,允许的顶点最大数目为vertexMaxNum,
// infinit表示无穷大,边数为0构造有向网
AdjListDirNetwork(int vertexMaxNum = DEFAULT_SIZE,
WeightType infinit = (WeightType)DEFAULT_INFINITY);
// 构造允许的顶点最大数目为vertexMaxNum,infinit表示无穷大,边数为0的有向网
~AdjListDirNetwork(); // 析构函数
void Clear(); // 清空有向网
bool IsEmpty(); // 判断有向网是否为空
int GetOrder(ElemType &d) const; // 求顶点的序号
Status GetElem(int v, ElemType &d) const; // 求顶点的元素值
Status SetElem(int v, const ElemType &d); // 设置顶点的元素值
WeightType GetInfinity() const; // 取无穷大的值
int GetVexNum() const; // 求有向网的顶点个数
int GetArcNum() const; // 求有向网的边数个数
int FirstAdjVex(int v) const; // 求有向网中顶点v的第一个邻接点
int NextAdjVex(int v1, int v2) const; // 求有向网中顶点v1的相对于v2的下一个邻接点
void InsertVex(const ElemType &d); // 插入元素值为d的顶点
void InsertArc(int v1, int v2, WeightType w);// 插入从顶点为v1到v2、权为w的边
void DeleteVex(const ElemType &d); // 删除元素值为d的顶点
void DeleteArc(int v1, int v2); // 删除从顶点为v1到v2的边
WeightType GetWeight(int v1, int v2) const; // 求从顶点为v1到v2的边的权值
void SetWeight(int v1, int v2, WeightType w);// 设置从顶点为v1到v2的边的权值
Status GetTag(int v) const; // 求顶点v的标志
void SetTag(int v, Status tag) const; // 设置顶点v的标志为tag
AdjListDirNetwork(const AdjListDirNetwork<ElemType, WeightType>