关于tarjan算法的空间优化
最近随着对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算法的空间优化的更多相关文章
- 近期公共祖先(LCA)——离线Tarjan算法+并查集优化
一. 离线Tarjan算法 LCA问题(lowest common ancestors):在一个有根树T中.两个节点和 e&sig=3136f1d5fcf75709d9ac882bd8cfe0 ...
- POJ 2553 The Bottom of a Graph TarJan算法题解
本题分两步: 1 使用Tarjan算法求全部最大子强连通图.而且标志出来 2 然后遍历这些节点看是否有出射的边,没有的顶点所在的子强连通图的全部点,都是解集. Tarjan算法就是模板算法了. 这里使 ...
- tarjan算法总结
部分内容引自https://www.cnblogs.com/stxy-ferryman/p/7779347.html Tarjan算法不是一个算法而是一类算法 1.求取强连通分量 强连通分量————有 ...
- tarjan算法--求解无向图的割点和桥
1.桥:是存在于无向图中的这样的一条边,如果去掉这一条边,那么整张无向图会分为两部分,这样的一条边称为桥 也就是说 无向连通图中,如果删除某边后,图变成不连通,则称该边为桥 2.割点:无向连通图中,如 ...
- tarjan算法求最近公共祖先
tarjian算法 LCA: LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通往根的道路上,肯定会有公共的节点,我们 ...
- 割点(Tarjan算法)【转载】
本文转自:www.cnblogs.com/collectionne/p/6847240.html 供大家学习 前言:之前翻译过一篇英文的关于割点的文章(英文原文.翻译),但是自己还有一些不明白的地方, ...
- 【强连通分量】tarjan算法及kosaraju算法+例题
阅读前请确保自己知道强连通分量是什么,本文不做赘述. Tarjan算法 一.算法简介 Tarjan算法是一种由Robert Tarjan提出的求有向图强连通分量的时间复杂度为O(n)的算法. 首先我们 ...
- 割点(Tarjan算法)
本文可转载,转载请注明出处:www.cnblogs.com/collectionne/p/6847240.html .本文未完,如果不在博客园(cnblogs)发现此文章,请访问以上链接查看最新文章. ...
- Java虚拟机内存基础、垃圾收集算法及JVM优化
1 JVM 简单结构图 1.1 类加载子系统与方法区 类加载子系统负责从文件系统或者网络中加载 Class 信息,加载的类信息存放于一块称 为方法区的内存空间.除了类的信息外,方法区中可能还会存放 ...
随机推荐
- java课程设计团队博客
java课程设计 本组主题:Calculator(简易计算器) 功能要求:基本实现计算器的功能,可运行加.减.乘.除.求倒数.求平方根.求百分号运算.正负数运算等 一.团队介绍 团队名称:熬夜做不出随 ...
- C#核心语法讲解-泛型(详细讲解泛型方法、泛型类、泛型接口、泛型约束,了解协变逆变)
泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个具 ...
- 纳税服务系统【信息发布管理、Ueditor、异步信息交互】
需求分析 我们现在来到了纳税服务系统的信息发布管理模块,首先我们跟着原型图来进行需求分析把: 一些普通的CRUD,值得一做的就是状态之间的切换了.停用和发布切换. 值得注意的是:在信息内容中,它可以带 ...
- 纳税服务系统【用户模块之使用POI导入excel、导出excel】
前言 再次回到我们的用户模块上,我们发现还有两个功能没有完成: 对于将网页中的数据导入或导出到excel文件中,我们是完全没有学习过的.但是呢,在Java中操作excel是相对常用的,因此也有组件供我 ...
- Oracle单引号的用法-转义
在ORACLE中,单引号有两个作用: 1:字符串是由单引号引用 2:转义. 单引号的使用是就近配对.而在单引号充当转义角色时相对不好理解 1.从第二个单引号开始被视为转义符,如果第二个单引号后面还 ...
- HTML基础入门
1.什么是HTML 2.HTML文件结构 3.HTML文档 4.HTML标签 1.什么是HTML 首先,HTML是一种语言,是用来描述网页的语言 HTML 指的是超文本标记语言 (Hyper Text ...
- open和opener使用說明
父網頁:window.open("article.html")子網頁:var dialoginfo=$('#dialogdata',window.opener.document); ...
- cocos2dx 播放gif
起因 或许有人会说,cocos2dx中直接帧动画就行了用什么GIF. 起因是为游戏内部要用到第三方平台的头像,而第三方平台的头像大多都是用到Gif,所以才会有了这个需求 过程 查了各种文档都没找到.但 ...
- Java中的HTTP通信技术详解
1.使用HTTP的Get方式读取网络数据 class ReadByGet extends Thread{ @Override public void run(){ URL url = n ...
- Vue 开发常见问题集锦
涉及技术栈 CLI: Vue-CLI UI: Element HTML: Pug(Jade) CSS: Less JavaScript: ES6 正文: polyfill 与 transform-ru ...