最近随着对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. 201521123033《Java程序设计》第1周学习总结

    1. 本周学习总结 (1)了解了JVM,JRE,JDK (2)配置java环境变量 (3)编译一些简单的java代码 2. 书面作业 为什么java程序可以跨平台运行?执行java程序的步骤是什么?( ...

  2. Java课程设计——GUI密码生成器201521123035

    1.团队课程设计博客链接 (http://www.cnblogs.com/wuling15/p/7061857.html) 2.个人负责模块或任务说明 (1)确定课题并进行任务分工 (2)编写随机数产 ...

  3. 201521123009 《Java程序设计》第9周学习总结

    1. 本周学习总结 2. 书面作业 本次PTA作业题集异常 Q1:常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代码中经常出现什么异常.需要捕获吗(为什么)?应如何 ...

  4. 201521123018 《Java程序设计》第11周学习总结

    1. 本章学习总结 你对于本章知识的学习总结 2. 书面作业 一.互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) 1.1 除了使用synchronized修饰方法实现互斥同步访问, ...

  5. Eclipse rap 富客户端开发总结(5): RAP国际化之路

    Eclipse RCP 中的plugin.xml国际化实现 1.  在工程的根目录下面建立一个plugin.properties资源文件:在此资源文件中写入需要国际化的内容(键/值对),举例如下: h ...

  6. Python数据类型方法精心整理,不必死记硬背,看看源码一切都有了

    Python认为一切皆为对象:比如我们初始化一个list时: li = list('abc') 实际上是实例化了内置模块builtins(python2中为__builtin__模块)中的list类: ...

  7. Linux Ubuntu从零开始部署web环境及项目 -----快捷键设置(四)

    上篇将了如何在linux部署web项目,这篇介绍如何设置常用快捷键 一.路径快捷键设置 临时快捷键设置:  执行XShel,输入: alias 'aa=cd /etc/sysconfig'       ...

  8. Maven(五)之Maven配置阿里云镜像飞快下jar包

    用过Maven的人都知道Maven对于依赖的管理让我们程序员从此远离了自己去在项目中把需要的jar包导入到项目中,但是因为中央仓库是在国外的,所以在我们从中央仓库下载依赖的时候, 我们发现下载速度真的 ...

  9. PuTsangTo-单撸游戏开发03 碰撞与跳跃瑕疵版

    继续上一部分,游戏的定位是横版平台动作类游戏,所以得有跳跃动作,首先想到的就是物理引擎,不过在2D游戏里,仅为了角色的跳跃而引入物理引擎,目前想来有些不至于,仅使用cocos默认带有的碰撞系统也足够了 ...

  10. Azure SQL Database (25) Azure SQL Database创建只读用户

    <Windows Azure Platform 系列文章目录> 本文将介绍如何在Azure SQL Database创建只读用户. 请先按照笔者之前的文章:Azure SQL Databa ...