好用且实用的链式前向星(好像就是邻接表)

对于某一个图,我们存储它的方式有很多种,其中常用的有

1、邻接矩阵(建图简单,使用简单,但是遍历图时间复杂度太大)

2、邻接表(建图较复杂,对于特殊的米字图时间复杂度太大)

3、优化的邻接表,在我前面一片文章里有介绍,就是为每一个节点保存一个last数组(最后的点编号),可以极大提高建图速度

so 4、链式前向星,只是保存边,构图简单,使用简单,时间复杂度只与边有关。


建图:

IL void add(int x,int y,int z){
    e[++cnt].to=y;
    e[cnt].next=head[x]; 
    e[cnt].w=z;
    head[x]=cnt;
    return ;
}
int main(){
  for(int i=1;i<n;i++){
    	scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
}

to记录某条边的后点,next将与某个前点所有关联的后点连接起来,head记录每个前点的起始编号,以便从后往前搜索所有关联点

遍历图程序如下:

  for(int t=1;t<=n;t++)
   for( int i=head[t];i;i=e[i].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)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值