邻接表的基本概念
定义
邻接表是一种图的存储结构,它使用"链表+数组"的组合方式来存储图中的顶点和边信息。每个顶点对应一个链表,链表中存储与该顶点直接相连的所有邻接顶点。
核心思想
- 数组部分:存储所有顶点信息
- 链表部分:每个顶点维护一个链表,存储从该顶点出发的所有边
数学表示
对于图 G = (V, E),其中:
- V = {v₁, v₂, ..., vₙ} 是顶点集合
- E 是边集合
邻接表为每个顶点 vᵢ 维护一个邻接链表,包含所有与 vᵢ 相邻的顶点。
1.头文件引入
cpp
#include<iostream> // 提供输入输出功能(如cout)
#include<string> // 提供字符串处理功能
using namespace std; // 使用标准命名空间,避免重复写std::
比喻 :就像建设城市交通网前,需要准备施工图纸 和材料清单。
2. 图类定义与内部结构
cpp
class Graph
{
private:
struct EdgeNode // 边结点结构:记录每条路线的详细信息
{
int vertex; // 目标顶点编号(路线终点站)
int weight; // 边权重(路线距离或成本)
EdgeNode* next; // 指向下一条边(下一条路线)
};
struct VertexNode // 顶点结点结构:每个交通枢纽站
{
int vertex; // 顶点编号(站点代码)
EdgeNode* firstEdge; // 指向该站发出的第一条路线
};
int vertices; // 顶点总数(枢纽站数量)
VertexNode* nodes; // 动态顶点数组(所有站点集合)
比喻:
VertexNode= 交通枢纽总站(如北京西站)EdgeNode= 班次时刻表(记录发往哪个城市、距离多少)nodes数组 = 全国所有火车站的集合
3. 构造函数:初始化交通网络
cpp
Graph::Graph(int vertices) // 参数:要创建的顶点数量
{
this->vertices = vertices; // 设置顶点总数
this->nodes = new VertexNode[vertices]; // 动态创建顶点数组
for (int i = 0; i < vertices; i++)
{
nodes[i].vertex = i; // 为每个顶点编号(0,1,2...)
nodes[i].firstEdge = NULL; // 初始时没有发出的边
}
};
比喻 :政府规划建设4个城市的交通枢纽站,目前只建好了空车站大楼 ,还没有开通任何班次。
4. 析构函数:拆除整个网络
cpp
Graph::~Graph() // 对象销毁时自动调用,清理内存
{
for (int i = 0; i < vertices; i++) // 遍历每个顶点
{
EdgeNode* curr = nodes[i].firstEdge; // 获取该顶点的第一条边
while (curr) // 当还有边结点存在时循环
{
EdgeNode* tmp = curr; // 临时保存当前边结点
curr = curr->next; // 指针移动到下一个边结点
delete tmp; // 删除当前边结点
}
}
delete[] nodes; // 删除整个顶点数组
}
比喻:城市拆迁时,需要:
- 逐个车站清理所有班次时刻表(边结点)
- 最后推平车站建筑(顶点数组)
- 顺序不能错,否则会造成内存浪费
5. 添加边:开通新路线
cpp
void Graph::addEdge(int u, int v, int weight)
{
EdgeNode* newnode = new EdgeNode; // 创建新的边结点
newnode->vertex = v; // 设置目标顶点(路线终点)
newnode->weight = weight; // 设置边权重(路线距离)
newnode->next = nodes[u].firstEdge; // 新结点指向原第一条边
nodes[u].firstEdge = newnode; // 更新头指针指向新结点
}
比喻:北京站(u=0)新开通到上海(v=1)的高铁:
- 制作新班次表(创建EdgeNode)
- 填写目的地和里程(vertex=v, weight=10)
- 插到最前面:新班次表放在现有班次表前面(头插法)
- 更新站内指示牌(修改firstEdge指针)
6. 打印边:显示所有路线信息
cpp
void Graph::printEdge()
{
for (int i = 0; i < vertices; i++) // 遍历每个顶点
{
EdgeNode* curr = nodes[i].firstEdge; // 获取该顶点第一条边
cout << "Vertex" << i << ":"; // 打印顶点编号
while (curr) // 遍历该顶点的所有边
{
// 打印目标顶点和权重
cout << curr->vertex << "(" << curr->weight << ") ";
curr = curr->next; // 移动到下一条边
}
cout << endl; // 换行,准备打印下一个顶点
}
}
比喻:记者走访每个火车站,记录发车情况:
- "Vertex0:2(20) 1(10)" = 北京站:发往广州(20km)、上海(10km)
- 使用while循环逐个查看所有班次表
7. 主函数:测试交通网络
cpp
int main()
{
Graph g(4); // 创建4个顶点的图(建设4个城市交通网)
// 添加5条交通路线(起点,终点,距离)
g.addEdge(0, 1, 10); // 北京→上海,10公里
g.addEdge(0, 2, 20); // 北京→广州,20公里
g.addEdge(1, 3, 30); // 上海→深圳,30公里
g.addEdge(2, 3, 40); // 广州→深圳,40公里
g.addEdge(1, 2, 50); // 上海→广州,50公里
g.printEdge(); // 打印整个交通网络
return 0; // 程序正常结束
}
8. 程序输出结果
cpp
Vertex0:2(20) 1(10) ← 北京站发往广州、上海
Vertex1:2(50) 3(30) ← 上海站发往广州、深圳
Vertex2:3(40) ← 广州站发往深圳
Vertex3: ← 深圳站为终点站,无发出路线
9. 关键特点总结
- 头插法效率高:新边插入链表头部,时间复杂度O(1)
- 内存动态管理:使用new/delete手动管理内存
- 逆序存储:后添加的边在链表前面(与添加顺序相反)
- 避免内存泄漏:析构函数确保所有动态内存被正确释放
现实比喻 :这套系统就像铁路调度管理系统,高效管理复杂的交通网络关系!

