算法提高之祖孙询问(附LCA)

算法提高之祖孙询问(附LCA)

  • 核心思想:最近公共祖先(LCA)

LCA算法

  • 向上标记法(不常用):一遍用bool数组从下往上不断标记祖先节点,再一遍再标记

  • 倍增法:类似快速幂,讲究的是二进制拼凑出一个数

    • 预处理ja[i][j] 表示从第i个节点向上跳2j层能跳到的节点

      • ja[i][j] = ja[ja[i][j-1][j-1] 先跳j-1次,再跳j-1次

      • 在这里插入图片描述

      • 例如跳11层,k从大到小遍历跳2k , 即先跳8层再跳2层最后跳1层

    • 操作步骤:

      • 1.深的节点先跳到和浅的节点同一层

      • 2.一起往上跳公共祖先节点下面的一层

      • 在这里插入图片描述

  •   #include <iostream>
      #include <cstring>
      #include <cstdio>
      #include <algorithm>
      
      using namespace std;
      const int N = 40010,M = N*2;
      
      int n,m;
      int q[N];
      int depth[N],fa[N][16];
      int h[N],e[M],ne[M],idx;
      
      void add(int a, int b)
      {
          e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
      }
      void bfs(int root)  //预处理ja和depth
      {
          memset(depth, 0x3f, sizeof depth);
          depth[0] = 0, depth[root] = 1;  //哨兵depth[0] = 0;防止跳出树
          int hh = 0, tt = 0;
          q[0] = root;
          while (hh <= tt)
          {
              int t = q[hh ++ ];
              for (int i = h[t]; ~i; i = ne[i])
              {
                  int j = e[i];
                  if (depth[j] > depth[t] + 1)
                  {
                      depth[j] = depth[t] + 1;
                      q[ ++ tt] = j;
                      fa[j][0] = t;
                      for (int k = 1; k <= 15; k ++ )
                          fa[j][k] = fa[fa[j][k - 1]][k - 1];
                  }
              }
          }
      }
      
      int lca(int a, int b)
      {
          if (depth[a] < depth[b]) swap(a, b);  //从小往上跳
          for (int k = 15; k >= 0; k -- )  //先大跳后小跳
              if (depth[fa[a][k]] >= depth[b])  //跳2^k步后仍在b下面 就可以跳
                  a = fa[a][k];
          if (a == b) return a;  //当前b(=a)点位置就是祖先
          for (int k = 15; k >= 0; k -- )  //再一起往上跳
              if (fa[a][k] != fa[b][k])  //没有跳到同一个点
              {
                  a = fa[a][k];
                  b = fa[b][k];
              }
          return fa[a][0];  //当前跳到公共祖先下一层 再向上跳2^0 = 1层即可
      }
      int main()
      {
          scanf("%d", &n);
          int root = 0;
          memset(h, -1, sizeof h);
      
          for (int i = 0; i < n; i ++ )
          {
              int a, b;
              scanf("%d%d", &a, &b);
              if (b == -1) root = a;
              else add(a, b), add(b, a);
          }
      
          bfs(root);
      
          scanf("%d", &m);
          while (m -- )
          {
              int a, b;
              scanf("%d%d", &a, &b);
              int p = lca(a, b);  //得到ab的最近公共祖先p
              if (p == a) puts("1");
              else if (p == b) puts("2");
              else puts("0");
          }
      
          return 0;
      }
    
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
LCA(最近公共祖先)是指在一棵树中,找到两个节点的最近的共同祖先节点。而Tarjan算法是一种用于求解强连通分量的算法,通常应用于有向图中。它基于深度优先搜索(DFS)的思想,通过遍历图中的节点来构建强连通分量。Tarjan算法也可以用于求解LCA问题,在有向无环图(DAG)中。 具体来说,在使用Tarjan算法求解LCA时,我们需要进行两次DFS遍历。首先,我们从根节点开始,遍历每个节点,并记录每个节点的深度(即从根节点到该节点的路径长度)。然后,我们再进行一次DFS遍历,但这次我们在遍历的过程中,同时进行LCA的查找。对于每个查询,我们将两个待查询节点放入一个查询列表中,并在遍历过程中记录每个节点的祖先节点。 在遍历的过程中,我们会遇到以下几种情况: 1. 如果当前节点已被访问过,说明已经找到了该节点的祖先节点,我们可以更新该节点及其所有后代节点的祖先节点。 2. 如果当前节点未被访问过,我们将其标记为已访问,并将其加入到查询列表中。 3. 如果当前节点有子节点,我们继续递归遍历子节点。 最终,对于每个查询,我们可以通过查询列表中的两个节点的最近公共祖先节点来求解LCA。 需要注意的是,Tarjan算法的时间复杂度为O(V+E),其中V为节点数,E为边数。因此,对于大规模的树结构,Tarjan算法是一种高效的求解LCA问题的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阳光男孩01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值