最近随着对tarjan算法理解的加深,我发现用另外一种途径实现tarjan的方法,且可以省去DFN数组,大大节省了空间。经过大量测试,已经无误。以下将分阶段阐述进行优化的过程。

第一阶段

下面来说一下我做出此优化的思路。设任意两个节点为u,v。纵观整个tarjan算法,我们发现,DFN数组被调用的地方只有两个:在搜索中将DFN[u]与low[v]比较大小和在回溯中与low[u]比较是否相等。我在这里将DFN的职责分别分给low与flag。

(一)在比较大小时将low[v],low[u]直接比较,经过思考我们可以发现,在搜索中,即在不重复访问节点时,比较DFN[u]和low[v]与直接比较low[u],low[v]是等效的。而同时将DFN反映是否访问过某个节点的功能交给flag,访问过的节点记flag为2。

(二)关于在回溯中与low[u]比较是否相等,从本质上探究这一操作,我发现这实际上是一种确认,即确定这个节点的low是否被修改过。由此,我们也可以将这个职责分给flag,记被修改过low的,存在于栈中的节点的flag为-1。到这一步,DFN就没有存在的必要了。

综上所述,将flag数组从bool型改为int型,综合算下来能省下一个bool型数组的空间大小。但仍然省的不多,因此有了第二阶段的优化。以下是目前的代码:(m为边数,n为顶点数,Anemone为tarjan函数,near是邻接表,个人习惯,求原谅orz)

(别着急,第二阶段将在代码后面继续论述,对这个代码不感兴趣的大佬们也可以跳过直接看第二阶段。)

#include<iostream>
#include<cstdio>
using namespace std;
struct near
{
int num,nex;
}ne[10000010];
int h[10000010],flag[10000010],low[10000010],z[10000010],wh=0,cont=0;
int Anemone(int x)
{
int no,mem;
wh++;
cont++;
z[cont]=ne[x].num;
flag[ne[x].num]++;
low[ne[x].num]=wh;
no=h[ne[x].num];
for(;;)
{
if(no==0)
{
break;
}
if(flag[ne[no].num]==0)
{
mem=Anemone(no);
if(mem<low[ne[x].num])
{
low[ne[x].num]=mem;
flag[ne[x].num]=-1;
}
}
else if(flag[ne[no].num]==1||flag[ne[no].num]==-1)
{
if(low[ne[no].num]<low[ne[x].num])
{
low[ne[x].num]=low[ne[no].num];
flag[ne[x].num]=-1;
}
}
no=ne[no].nex;
}
if(flag[ne[x].num]==1)
{
for(;;)
{
printf("%d ",z[cont]);
if(flag[z[cont]]==1)
{
flag[z[cont]]=2;
cont--;
break;
}
else
{
flag[z[cont]]=2;
cont--;
}
}
printf("\n");
}
return low[ne[x].num];
}
int main()
{
freopen("yangli.out","r",stdin);
freopen("dan.out","w",stdout);
int n,m,mem,no;
scanf("%d%d",&n,&m);
int i,x,y;
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
mem=h[x];
h[x]=i;
ne[i].num=y;
ne[i].nex=mem;
}
for(i=1;i<=n;i++)
{
if(flag[i]==0)
{
wh++;
cont++;
z[cont]=i;
flag[i]++;
low[i]=wh;
no=h[i];
for(;;)
{
if(no==0)
{
break;
}
if(flag[ne[no].num]==0)
{
mem=Anemone(no);
if(mem<low[i])
{
low[i]=mem;
flag[i]=-1;
}
}
else if(flag[ne[no].num]==1||flag[ne[no].num]==-1)
{
if(low[ne[no].num]<low[i])
{
low[i]=low[ne[no].num];
flag[i]=-1;
}
}
no=ne[no].nex;
}
if(flag[i]==1)
{
for(;;)
{
printf("%d ",z[cont]);
if(flag[z[cont]]==1)
{
flag[z[cont]]=2;
cont--;
break;
}
else
{
flag[z[cont]]=2;
cont--;
}
}
printf("\n");
}
}
}
return 0;
}

第二阶段(见证奇迹的时刻!(大雾))

仔细观察第一阶段,可以看出,flag的值实际上只有四种值,即-1,0,1,2。这么几个值就开个int数组真的是相当的浪费,但却又没有办法使用bool,处于非常尴尬的境地。这时候,我突然想到了一种魔性做法--使用char型变量存储!

char型只占一个字节,用来干这种事可谓再好不过了。需要注意的是,char值不能为-1(好像不能吧,记不清了,记错了大家也别那么较真),因此将-1,0,1,2对应的改为0,1,2,3。

最后,终于成功省掉了相当于一个ing型数组大小的内存。下面是最终代码:(m为边数,n为顶点数,Anemone为tarjan函数,near是邻接表,个人习惯,求原谅orz)

#include<iostream>
#include<cstdio>
using namespace std;
struct near
{
int num,nex;
}ne[];
int h[],low[],z[],wh=,cont=;
char flag[];
int Anemone(int x)
{
int no,mem;
wh++;
cont++;
z[cont]=ne[x].num;
flag[ne[x].num]=;
low[ne[x].num]=wh;
no=h[ne[x].num];
for(;;)
{
if(no==)
{
break;
}
if(flag[ne[no].num]==)
{
mem=Anemone(no);
if(mem<low[ne[x].num])
{
low[ne[x].num]=mem;
flag[ne[x].num]=;
}
}
else if(flag[ne[no].num]==||flag[ne[no].num]==)
{
if(low[ne[no].num]<low[ne[x].num])
{
low[ne[x].num]=low[ne[no].num];
flag[ne[x].num]=;
}
}
no=ne[no].nex;
}
if(flag[ne[x].num]==)
{
for(;;)
{
printf("%d ",z[cont]);
if(flag[z[cont]]==)
{
flag[z[cont]]=;
cont--;
break;
}
else
{
flag[z[cont]]=;
cont--;
}
}
printf("\n");
}
return low[ne[x].num];
}
int main()
{
freopen("yangli.out","r",stdin);
freopen("dan.out","w",stdout);
int n,m,mem,no;
scanf("%d%d",&n,&m);
int i,x,y;
for(i=;i<=m;i++)
{
scanf("%d%d",&x,&y);
mem=h[x];
h[x]=i;
ne[i].num=y;
ne[i].nex=mem;
}
for(i=;i<=n;i++)
{
flag[i]=;
}
for(i=;i<=n;i++)
{
if(flag[i]==)
{
wh++;
cont++;
z[cont]=i;
flag[i]=;
low[i]=wh;
no=h[i];
for(;;)
{
if(no==)
{
break;
}
if(flag[ne[no].num]==)
{
mem=Anemone(no);
if(mem<low[i])
{
low[i]=mem;
flag[i]=;
}
}
else if(flag[ne[no].num]==||flag[ne[no].num]==)
{
if(low[ne[no].num]<low[i])
{
low[i]=low[ne[no].num];
flag[i]=;
}
}
no=ne[no].nex;
}
if(flag[i]==)
{
for(;;)
{
printf("%d ",z[cont]);
if(flag[z[cont]]==)
{
flag[z[cont]]=;
cont--;
break;
}
else
{
flag[z[cont]]=;
cont--;
}
}
printf("\n");
}
}
}
return ;
}

关于tarjan算法的空间优化的更多相关文章

  1. 近期公共祖先(LCA)——离线Tarjan算法+并查集优化

    一. 离线Tarjan算法 LCA问题(lowest common ancestors):在一个有根树T中.两个节点和 e&sig=3136f1d5fcf75709d9ac882bd8cfe0 ...

  2. POJ 2553 The Bottom of a Graph TarJan算法题解

    本题分两步: 1 使用Tarjan算法求全部最大子强连通图.而且标志出来 2 然后遍历这些节点看是否有出射的边,没有的顶点所在的子强连通图的全部点,都是解集. Tarjan算法就是模板算法了. 这里使 ...

  3. tarjan算法总结

    部分内容引自https://www.cnblogs.com/stxy-ferryman/p/7779347.html Tarjan算法不是一个算法而是一类算法 1.求取强连通分量 强连通分量————有 ...

  4. tarjan算法--求解无向图的割点和桥

    1.桥:是存在于无向图中的这样的一条边,如果去掉这一条边,那么整张无向图会分为两部分,这样的一条边称为桥 也就是说 无向连通图中,如果删除某边后,图变成不连通,则称该边为桥 2.割点:无向连通图中,如 ...

  5. tarjan算法求最近公共祖先

    tarjian算法 LCA: LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通往根的道路上,肯定会有公共的节点,我们 ...

  6. 割点(Tarjan算法)【转载】

    本文转自:www.cnblogs.com/collectionne/p/6847240.html 供大家学习 前言:之前翻译过一篇英文的关于割点的文章(英文原文.翻译),但是自己还有一些不明白的地方, ...

  7. 【强连通分量】tarjan算法及kosaraju算法+例题

    阅读前请确保自己知道强连通分量是什么,本文不做赘述. Tarjan算法 一.算法简介 Tarjan算法是一种由Robert Tarjan提出的求有向图强连通分量的时间复杂度为O(n)的算法. 首先我们 ...

  8. 割点(Tarjan算法)

    本文可转载,转载请注明出处:www.cnblogs.com/collectionne/p/6847240.html .本文未完,如果不在博客园(cnblogs)发现此文章,请访问以上链接查看最新文章. ...

  9. Java虚拟机内存基础、垃圾收集算法及JVM优化

    1 JVM 简单结构图   1.1 类加载子系统与方法区 类加载子系统负责从文件系统或者网络中加载 Class 信息,加载的类信息存放于一块称 为方法区的内存空间.除了类的信息外,方法区中可能还会存放 ...

随机推荐

  1. 201521123019 《java程序设计》 第13周学习总结

    1. 本章学习总结 2. 书面作业 Q1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu.edu.cn,分析返回结果有何不同?为什么会有这样的不同? 访问cec ...

  2. 201521123102 《Java程序设计》第9周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2.书面作业 1.常用异常 题目5-1 1.2 自己以前编写的代码中经常出现什么异常.需要捕获吗(为什么)?应如何避 ...

  3. 201521123015 《Java程序设计》第13周学习总结

    1. 本周学习总结 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu.edu.cn,分析返回结果有何不同?为什么会有这样的不同? IP地址不同 ...

  4. org.springframework.core.NestedIOException: ASM ClassReader failed to parse class file - probably du

    如果出现类似下面的错误,原因就是JDK版本太高了,我换成1.7就没事了 Caused by: org.springframework.core.NestedIOException: ASM Class ...

  5. 支持语音识别、自然语言理解的微信小程序(“遥知之”智能小秘)完整源码分享

    记录自己搭建https的silk录音文件语音识别服务的调用过程,所有代码可在文中找链接打包下载 >>>>>>>>>>>>> ...

  6. 初识Hibernate之环境搭建

         相信所有做后端的程序员同行们,没有不知道Hibernate大名的.这是一个经典的轻量级Java EE持久层的解决方案,它使得我们程序员能以面向对象的思维操作传统的关系型数据库,这也是其存在的 ...

  7. python pyinstaller打包exe暗坑1

    环境 python2.7.9 win-xp 今天打包了一个小脚本,结果打开报错

  8. 记录maven 整合SSM框架

    一.新建maven项目 建好的项目结构如下图: 还需要做以下配置: 勾选上这两项后,就会自动生成 "src/main/java"   和 "src/main/resour ...

  9. Java中的对象和引用

    <Java编程思想>中有一段关于对象的说法: "按照通俗的说法,每个对象都是某个类(class)的一个实例(instance),这里,'类'就是'类型'的同义词." 简 ...

  10. 编码格式简介:ASCII码、ANSI、GBK、GB2312、GB18030和Unicode、UTF-8,BOM头

    编码格式简介:ASCII码.ANSI.GBK.GB2312.GB18030和Unicode.UTF-8,BOM头 二进制: 只有0和1. 十进制.十六进制.八进制: 计算机其实挺笨的,它只认识0101 ...