链式前向星与SPFA

一.链式前向星

链式前向星

链式前向星通过数组模拟链表的方式存储图。它的核心思想是:

  • 使用 head 数组记录每个节点的第一条边。

  • 使用 next 数组记录每条边的下一条边。

  • 使用 to 数组记录每条边的终点。

  • 使用 weight 数组记录每条边的权重。
     

    // 数据结构
    const int MAXN = 10005; // 最大节点数
    const int MAXM = 100005; // 最大边数
    
    int head[MAXN]; // head[u] 表示节点 u 的第一条边的编号
    int next[MAXM]; // next[e] 表示边 e 的下一条边的编号
    int to[MAXM];   // to[e] 表示边 e 的终点
    int weight[MAXM]; // weight[e] 表示边 e 的权重
    int cnt = 0; // 边的计数器
    
    
    // 添加边
    void addEdge(int u, int v, int w) {
        to[cnt] = v;          // 记录终点
        weight[cnt] = w;      // 记录权重
        next[cnt] = head[u];  // 将新边插入链表头部
        head[u] = cnt++;      // 更新 head[u]
    }

    二.SPFA 算法
     

    SPFA 是 Bellman-Ford 算法的优化版本,通过队列避免了对所有边进行不必要的松弛操作。

    算法思想
  • 初始化

    • 设源点为 s,初始化距离数组 dist,其中 dist[s] = 0,其余节点的距离为无穷大(INF)。

    • 将源点 s 加入队列。

  • 松弛操作

    • 从队列中取出一个节点 u,遍历其所有邻接边 (u, v, w)

    • 如果 dist[u] + w < dist[v],则更新 dist[v],并将 v 加入队列(如果 v 不在队列中)。

  • 检测负权回路

    • 如果某个节点被松弛超过 N-1 次,则说明图中存在负权回路。
       

      #include <iostream>
      #include <vector>
      #include <queue>
      #include <climits>
      using namespace std;
      
      const int MAXN = 10005; // 最大节点数
      const int MAXM = 100005; // 最大边数
      const int INF = INT_MAX; // 无穷大
      
      int head[MAXN]; // head[u] 表示节点 u 的第一条边的编号
      int next[MAXM]; // next[e] 表示边 e 的下一条边的编号
      int to[MAXM];   // to[e] 表示边 e 的终点
      int weight[MAXM]; // weight[e] 表示边 e 的权重
      int cnt = 0; // 边的计数器
      
      void addEdge(int u, int v, int w) {
          to[cnt] = v;          // 记录终点
          weight[cnt] = w;      // 记录权重
          next[cnt] = head[u];  // 将新边插入链表头部
          head[u] = cnt++;      // 更新 head[u]
      }
      
      bool spfa(int n, int s, vector<int>& dist) {
          dist.assign(n, INF); // 初始化距离数组
          dist[s] = 0; // 源点到自身的距离为 0
      
          queue<int> q;
          q.push(s); // 将源点加入队列
      
          vector<int> cntRelax(n, 0); // 记录每个节点的松弛次数
          vector<bool> inQueue(n, false); // 记录节点是否在队列中
          inQueue[s] = true;
      
          while (!q.empty()) {
              int u = q.front();
              q.pop();
              inQueue[u] = false;
      
              // 遍历 u 的所有邻接边
              for (int e = head[u]; e != -1; e = next[e]) {
                  int v = to[e];
                  int w = weight[e];
      
                  if (dist[u] + w < dist[v]) {
                      dist[v] = dist[u] + w;
      
                      // 如果 v 不在队列中,则加入队列
                      if (!inQueue[v]) {
                          q.push(v);
                          inQueue[v] = true;
      
                          // 检测负权回路
                          if (++cntRelax[v] >= n) {
                              return false; // 存在负权回路
                          }
                      }
                  }
              }
          }
      
          return true; // 不存在负权回路
      }
      
      int main() {
          int n, m, s;
          cout << "请输入节点数、边数和源点: ";
          cin >> n >> m >> s;
      
          // 初始化 head 数组
          fill(head, head + n, -1);
      
          cout << "请输入每条边的起点、终点和权重: " << endl;
          for (int i = 0; i < m; i++) {
              int u, v, w;
              cin >> u >> v >> w;
              addEdge(u, v, w); // 添加边
          }
      
       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值