求强连通分量的三种算法——Kosaraju, Tarjan, Gabow

就我所知,有三种时间复杂度为O(n)的方法可以求强连通分量,分别是Kosaraju、Tarjan和Gabow。

  1. Kosaraju
    算法的步骤为
    • 对图G进行DFS,并按照遍历完成的先后顺序进行标号。
    • 将图G中所有的边反向得到G'。
    • 对G'进行DFS,每轮DFS都选择编号最大的点最为当前的遍历树的根。
    • 最后,遍历得到的森林就是SCC的集合。
    该算法的优点在于,最后得到的节点是按照拓扑序组织好的,在求解2-SAT的过程中十分方便。
  2. Tarjan
    第一个求解SCC的算法,应用非常广泛,几乎任何和图的遍历有关的问题都可以套用Tarjan算法的思想(比如,求割点、桥、块等等)。
    在遍历时,对每一个节点定义时间戳,同时定义Low函数,含义为节点及其子孙通过非父子边的返祖边所能到达的最小时间戳。最后若Low函数等于其时间戳,则当前递归栈内存在一个SCC。
    代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    void dfs(intindex) {
      low[index] = dfn[index] = cnt++;
      used[index] = 1;
      del[index] = 0;
      st[top++] = index;
      for (inti = last[index]; i!=-1; i = e[i].nxt)
        if (!used[e[i].y]) {
          dfs(e[i].y);
          if (low[index]>low[e[i].y]) low[index] = low[e[i].y];
        }
        else if(!del[e[i].y]) if (low[index]>dfn[e[i].y])
          low[index] = dfn[e[i].y];
      if (low[index]==dfn[index]) {
        do {
          root[st[--top]] = index;
          del[st[top]] = 1;
        } while(st[top]!=index);
      }
    }
  3. Gabow
    算法的原理说起来太麻烦,还是直接给出代码。其实这个算法并不是很常用。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    #include <cstdio>
    #include <cstring>
    #include <stack>
      
    using namespace std;
      
    const int MAXN = 1000;
      
    struct node {
      int tar;
      node *nxt;
      node(int_tar,node *_nxt)
        : tar(_tar),nxt(_nxt) {}
    };
      
    node *g[MAXN];
    int n,m,co,all,dfn[MAXN],id[MAXN];
    stack<int> vec,path;
      
    void dfs(intx) {
      dfn[x] = co++;
      vec.push(x);
      path.push(x);
      for (node *i = g[x]; i!=NULL; i = i->nxt)
        if (dfn[i->tar]==-1) dfs(i->tar);
        else if(id[i->tar]==-1)
          while (dfn[path.top()]>dfn[i->tar]) path.pop();
      if (path.top()==x) path.pop();
      else return;
      for (inti; ;) {
        id[i = vec.top()] = all;
        vec.pop();
        if (i==x)break;
      }
      all++;
    }
      
    int main() {
      freopen("gabow.in","r",stdin);
      freopen("gabow.out","w",stdout);
      scanf("%d%d",&n,&m);
      memset(g,0,sizeof(g));
      for (inti = 0,a,b; i<m; i++) {
        scanf("%d%d",&a,&b); a--; b--;
        g[a] = newnode(b,g[a]);
      }
      co = all = 0;
      memset(dfn,-1,sizeof(dfn));
      memset(id,-1,sizeof(id));
      for (inti = 0; i<n; i++)
        if (dfn[i]==-1) dfs(i);
      for (inti = 0; i<n; i++)
        printf("%d\n",id[i]);
      return 0;
    }
转自: http://rchardx.is-programmer.com/posts/14898.html
### VSCode 常用插件推荐 以下是针对开发人员常用的几类功能所整理的 VSCode 插件推荐: #### 浏览器预览工具 对于前端开发者来说,在浏览器中实时查看 HTML 文件的效果是非常重要的。可以通过安装 **Live Server** 或者类似的插件来实现这一需求。这类插件允许通过快捷键或者鼠标右键操作快速在默认或自定义浏览器中打开 HTML 文件,支持多种主流浏览器,例如 Firefox、Chrome、Opera、IE 和 Safari [^1]。 #### 书签管理工具 为了提高代码编辑效率,可以使用 Bookmarks 插件标记重要位置并快速导航至这些标记处。具体配置方法如下:按下 `F1` 键调出命令面板,输入 `[打开键盘快捷方式]` 进入设置界面;随后可调整添加/删除标签以及跳转至上一/下一标签的相关快捷键组合。例如,将 PgUp 设置为跳转到上一个标签,PgDn 跳转到下一个标签,Ctrl+T 添加或移除标签 [^2]。 #### 中文化支持 如果希望让 VSCode 的菜单栏显示中文语言,则需执行以下步骤完成切换:按住 `Ctrl+Shift+P` 打开指令输入框,接着输入 `Configure Display Language` 并回车确认;之后从下拉列表里挑选目标语言包(如 zh-cn),最后按照提示点击重启按钮即可生效 [^3]。 此外还有其他一些非常实用但未提及于上述引用中的扩展程序也值得考虑加入日常工作中: - **Bracket Pair Colorizer**: 自动给匹配的大括号着色以便更容易识别嵌套结构。 - **ESLint / Prettier - Code formatter**: 配合 JavaScript (JSX), TypeScript, Vue.js 等项目进行静态分析和自动格式化处理。 - **GitLens — Git supercharged**: 提升版本控制体验,提供更深入的历史记录视图等功能。 ```javascript // 示例 ESLint 配置文件 .eslintrc.json 内容片段展示如何启用规则 { "rules": { "indent": ["error", 4], "quotes": ["error", "double"], "semi": ["error", "always"] } } ``` 以上列举了一些常见的 Visual Studio Code 插件及其用途介绍,实际应用过程中可根据个人偏好和技术栈特点灵活选用适合自己的辅助工具集。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值