关于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课设 学生基本信息管理 团队博客
1.成员 邹其元 网络1512 201521123060 杨钧宇 网络1512 201521123062 2.项目Git地址 团队项目码云地址 //添加截图 3. 项目git提交记录截图(要体现出每个 ...
- Python2.7笔记——常用技术点汇总
目录 · 概况 · 安装 · 基础 · 基础语法 · 数据类型 · 变量 · 常量 · 字符编码 · 字符串格式化 · list · tuple · dict · set · if语句 · for语句 ...
- MySQL数据库引擎介绍、区别
数据库引擎介绍 MySQL数据库引擎取决于MySQL在安装的时候是如何被编译的.要添加一个新的引擎,就必须重新编译MYSQL.在缺省情况下,MYSQL支持三个引擎:ISAM.MYISAM和HEAP.另 ...
- c# 浮点数计算问题
给大家看个计算题,看看大家的算术能力. 0.1 +0.1 +0.1 - 0.3 等于几? 大家可能会说这么简单的问题,是不是看不起我?肯定等于0啊. 如果大家直接算的是没有问题的,但是如果用计算机呢? ...
- JSP第七篇【简单标签、应用、DynamicAttribute接口】
为什么要用到简单标签? 上一篇博客中我已经讲解了传统标签,想要开发自定义标签,大多数情况下都要重写doStartTag(),doAfterBody()和doEndTag()方法,并且还要知道SKIP_ ...
- Apache Spark 2.2.0 中文文档 - Spark RDD(Resilient Distributed Datasets)论文 | ApacheCN
Spark RDD(Resilient Distributed Datasets)论文 概要 1: 介绍 2: Resilient Distributed Datasets(RDDs) 2.1 RDD ...
- DI in ASP.NET Core
.NET-Core Series Server in ASP.NET-Core DI in ASP.NET-Core Routing in ASP.NET-Core Error Handling in ...
- PHP防SQL注入攻击
PHP防SQL注入攻击 收藏 没有太多的过滤,主要是针对php和mysql的组合. 一般性的防注入,只要使用php的 addslashes 函数就可以了. 以下是一段copy来的代码: PHP代码 $ ...
- 系统学习java高并发系列三
转载请注明原创出处,谢谢! 首先需要说说线程安全?关于线程安全一直在提,比如StringBuilder和StringBuffer有什么区别? 经常就会出现关于线程安全与线程非安全,可能一直在提自己没有 ...
- 【京东详情页】——原生js爬坑之标签页
一.引言 要做详情页的商品评价等5个li的标签页转换,效果如下: 二.实现原理 有一个特别的地方:上面五个li,但下面只有四个容器(table/div). 设计的目的:无论点哪个li,只有前四个div ...