链式前向星与邻接表对比

本文图片及数据

对于这样一张有向图:
这里写图片描述

输入边的顺序如下:
1 2
2 3
3 4
1 3
4 1
1 5
4 5

对于邻接表来说是这样的:
1 -> 2 -> 3 -> 5
2 -> 3
3 -> 4
4 -> 1 -> 5
5 ->^

对于链式前向星来说是这样的:

edge[0].to = 2; edge[0].next = -1; head[1] = 0;
edge[1].to = 3; edge[1].next = -1; head[2] = 1;
edge[2].to = 4; edge[2],next = -1; head[3] = 2;
edge[3].to = 3; edge[3].next = 0; head[1] = 3;
edge[4].to = 1; edge[4].next = -1; head[4] = 4;
edge[5].to = 5; edge[5].next = 3; head[1] = 5;
edge[6].to = 5; edge[6].next = 4; head[4] = 6;

(以起点为1举例):
head[1] = 5 (以1为起点的第一条边的位置) -> edge[5].to = 5 (终点) -> edge[5].next = 3(同一起点下一条边的位置) ->edge[3].to = 3(第二条边的终点) -> edge[3].next = 0 -> edge[0].to = 2(第三条边的终点) ->edge[0].next = -1 (没下一条边)

简化来说就是:
1 -> 5 -> 3 -> 2
可以看到链式前向星和邻接表除了顺序不一样,其他的可以说是一样的。

看一下链式前向星的代码:

const int maxn = 10005;   //点的最大个数

int head[maxn], cnt=0;//head用来表示以i为起点的第一条边存储的位置,cnt读入边的计数器

struct Edge
{
    int next; //同一起点的上一条边的储存位置
    int to; //第i条边的终点
    int w; //第i条边权重
};
Edge edge[maxn];

void add(int u,int v,int w)  //读入边
{
    edge[cnt].w = w;
    edge[cnt].to = v;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}

void read()  //遍历所有边
{
    for(int i=0; i<=n; i++)
        for(int j=head[i]; j!=-1; j=edge[j].next)
}

我觉得链式前向星和邻接表的思想是一样的,区别就是:邻接表是用链表实现的,可以动态的增加边,
而链式前向星是用结构体数组实现的,是静态的,需要一开始知道数据范围,开好数组大小。
相比之下,邻接表灵活,链式前向星好写。

如有理解不对的地方,希望可以告诉我。

<think>我们可以使用链式前向星来替代邻接表实现Dijkstra算法。链式前向星是一种用数组模拟链表的方式存储图的方法,它比邻接表更节省内存,并且访问效率也较高,特别适合处理稀疏图。 链式前向星的核心是三个数组: - `head[u]`:记录节点u的第一条边的位置(在边数组中的索引) - `edge[i].to`:第i条边的终点 - `edge[i].next`:第i条边的下一条边的索引(在边数组中的索引) - `edge[i].w`:第i条边的权重 我们通常用一个结构体数组来存储边,或者用多个数组分别存储每条边的终点、下一条边的索引权重。 下面我们用链式前向星实现Dijkstra算法:</think>### 链式前向星实现Dijkstra算法 链式前向星是一种高效存储图的数据结构,特别适合处理稀疏图。相比邻接表,它更节省内存且访问效率高。以下是使用链式前向星实现的Dijkstra算法: ```cpp #include <iostream> #include <vector> #include <queue> #include <climits> #include <cstring> // 用于memset using namespace std; const int MAXN = 10005; // 最大节点数 const int MAXM = 200005; // 最大边数 // 链式前向星数据结构 struct Edge { int to; // 边的终点 int weight; // 边的权重 int next; // 下一条边的索引 } edges[MAXM]; // 边数组 int head[MAXN]; // 每个节点的第一条边索引 int edgeCount = 0; // 当前边数 // 添加边(有向图) void addEdge(int u, int v, int w) { edges[edgeCount].to = v; // 设置终点 edges[edgeCount].weight = w; // 设置权重 edges[edgeCount].next = head[u]; // 指向当前头节点 head[u] = edgeCount++; // 更新头节点索引 } // Dijkstra算法实现 vector<int> dijkstra(int start, int n) { // 初始化距离数组 vector<int> dist(n, INT_MAX); dist[start] = 0; // 优先队列(最小堆) priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; pq.push({0, start}); while (!pq.empty()) { int curDist = pq.top().first; int curNode = pq.top().second; pq.pop(); // 跳过无效节点 if (curDist > dist[curNode]) continue; // 遍历当前节点的所有邻接边(链式前向星遍历) for (int i = head[curNode]; i != -1; i = edges[i].next) { int neighbor = edges[i].to; int weight = edges[i].weight; int newDist = curDist + weight; // 松弛操作 if (newDist < dist[neighbor]) { dist[neighbor] = newDist; pq.push({newDist, neighbor}); } } } return dist; } int main() { // 初始化头节点数组(-1表示无出边) memset(head, -1, sizeof(head)); // 构建示例图(有向带权图) // 节点0→1(4), 0→2(1) addEdge(0, 1, 4); addEdge(0, 2, 1); // 节点1→3(1) addEdge(1, 3, 1); // 节点2→1(2), 2→3(5) addEdge(2, 1, 2); addEdge(2, 3, 5); // 节点3→4(3) addEdge(3, 4, 3); int n = 5; // 总节点数 int start = 0; // 起点 vector<int> dist = dijkstra(start, n); // 输出结果 cout << "从节点 " << start << " 出发的最短路径:" << endl; for (int i = 0; i < n; i++) { cout << "到节点 " << i << " : "; if (dist[i] == INT_MAX) cout << "不可达"; else cout << dist[i]; cout << endl; } return 0; } ``` ### 链式前向星核心解析 #### 1. **数据结构设计** ```cpp struct Edge { int to; // 边的终点 int weight; // 边的权重 int next; // 下一条边的索引 } edges[MAXM]; // 边数组 int head[MAXN]; // 每个节点的第一条边索引 ``` - **head[u]**:存储节点u的第一条边在edges数组中的索引 - **edges[i]**:存储第i条边的信息 - `to`:边的终点 - `weight`:边的权重 - `next`:下一条邻接边的索引(形成链表) #### 2. **添加边操作** ```cpp void addEdge(int u, int v, int w) { edges[edgeCount].to = v; edges[edgeCount].weight = w; edges[edgeCount].next = head[u]; // 新边指向当前头节点 head[u] = edgeCount++; // 更新头节点为新边 } ``` - **头插法**:新边插入链表头部 - **时间复杂度**:O(1) 常数时间添加边 #### 3. **遍历邻接边** ```cpp for (int i = head[curNode]; i != -1; i = edges[i].next) { int neighbor = edges[i].to; int weight = edges[i].weight; // ... 处理逻辑 ... } ``` - 从`head[curNode]`开始,沿着`next`指针遍历所有邻接边 - 遍历顺序添加顺序相反(后添加的边先访问) ### 链式前向星 vs 邻接表 | 特性 | 链式前向星 | 邻接表 | |------|------------|--------| | **内存分配** | 静态数组(预分配) | 动态vector | | **内存开销** | 更小(无额外指针) | 较大(vector开销) | | **访问效率** | 连续内存访问 | 可能内存不连续 | | **添加边** | O(1) | O(1)(均摊) | | **遍历邻接点** | O(degree) | O(degree) | | **适用场景** | 内存敏感/大图 | 开发便捷/小图 | ### 链式前向星的优点 1. **内存高效**:仅需存储必要信息,无额外指针开销 2. **缓存友好**:边数据存储在连续数组中 3. **静态内存**:避免动态内存分配开销 4. **适合大图**:可精确控制内存使用量 ### 注意事项 1. **初始化**:必须初始化`head`数组为-1 2. **无向图**:添加双向边(调用两次addEdge) 3. **索引范围**:节点编号通常从0或1开始 4. **内存预分配**:需预估最大边数(MAXM)
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值