我们知道,图的一种表示的数据结构是邻接链表。而谈到邻接链表,大家可能会对链表产生恐惧。较好地使用链表而不出现错误需要对链表原理的深刻理解和扎实的编程功底,那么我们该如何快速地学会使用邻接链表呢?
这里,我推荐使用标准模版库(STL)中的标准模版 std::vector。接下来,我们了解一些vector在实现邻接链表中的应用。
首先我们定义一个结构体,包括邻接结点和边权值,用来表示一条边。
struct Edge{
int nextNode; // 下一个结点编号
int cost; // 该边的权重
};
我们为每一个结点都建立一个单链表来保存与其相邻的边权值和结点的信息。我们使用vector来模拟这些单链表,利用如下语句为每一个结点都建立一个vector对象(结点数量为N)。
vector<Edge> edge[N];
该语句建立了一个大小为N的数组,而数组中保存的元素即为vector对象,我们用edge[i]的vector来表示为结点i建立的单链表。
为了使用vector我们还需在C++源文件头部添加相应的头文件。
下面,我们学习如何为这些“单链表”添加和删除信息。
利用
for(int i=0;i<N;i++){ // 遍历所有结点
eage[i].clear(); // 清空其单链表
}
来实现对这些单链表的初始化,即利用vector::clear()操作清空这些单链表。
当我们要向其中添加信息时,调用vector::push_back(Edge)。如下所示:
Edge tmp; // 准备一个Edge结构体
tmp.nextNode=3; // 下一结点编号为3
tmp.cost=38; // 该边权值为38;
edge[1].push_back(tmp); // 将该边加入结点1的单链表中
当我们需要查询某个结点的所有邻接信息时,则对vector进行遍历。
for(int i=0;i<edge[2].size();i++){ // 对edge[2]进行遍历,即对所有与结点2相邻的边进行遍历,edge[2].size()表示其大小
int nextNode=edge[2][i].nextNode; // 读出邻接结点
int cost=edge[2][i].cost; // 读出该边权值
}
可见,对使用vector实现的邻接链表的访问非常类似于对二维数组的访问,但是其每行的长度是根据边的数量动态变化的。
当我们需要删除某个单链表中的某些边信息时,我们调用vector::erase。
若我们要删除结点1的单链表中edge[1][i]所对应的边信息时,我们使用如下语句:
edge[1].erase(edge[1].begin()+i, edge[1].begin()+i+1); // 即vector.erase(vector.begin()+第一个要删除的元素编号,vector.begin()+最后一个要删除元素的编号+1
我们只要记住如上vector的基本用法,就能使用其来模拟单链表,并能对这些单链表进行清空、添加、删除、遍历等操作。