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

    1. 本章学习总结 1.第一次接触Java,初步了解Java的运行环境,学会了安装eclipse和JDK,解决了在安装中的path变量的设置问题. 2.知道了jvm,jre,jdk的区别,jdk是一个 ...

  2. Servlet的生命周期与运行原理

    Servlet的生命周期:    1 加载classLoader    2 实例化 new    3 初始化 init(ServletConfig)    4 处理请求 service doGet d ...

  3. 201521123027 <java程序设计>第九周学习总结

    1.本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2.书面作业 Q1.常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代码中经常出现什 ...

  4. Java课程设计 ————五子棋 (个人博客)

    JAVA课程设计 五子棋(博客个人版) •团队课程设计博客链接 http://www.cnblogs.com/mz201521044152/p/7065575.html •个人负责模块或任务说明 1. ...

  5. Java多线程高并发学习笔记(一)——Thread&Runnable

    进程与线程 首先来看百度百科关于进程的介绍: 进程是一个具有独立功能的程序关于某个数据集合的一次运行活动.它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体.它不只是程序的代码,还包括当前的 ...

  6. 巧用 BootStrap --- 栅格系统(布局)轻松搞定网页响应式布局!

    摘要:Bootstrap 为我们提供了一套响应式.移动设备优先的流式栅格系统,合理的使用栅格系统将会使得网站页面布局变得更加简单,在设置了媒体查询之后,响应式网站也无需再单独写了.接下来我以Boots ...

  7. 一、React Native 搭建开发环境(1)(Mac OS - IOS项目)

    React Native是Facebook推出的一个开发IOS和安卓APP的技术.至于更多的详情,这里不再描述,大家可以自行百度它的定义. 目的: 由于我想在一台电脑上同时开发IOS和Android两 ...

  8. 深入理解计算机系统(2.5)------C语言中的有符号数和无符号数以及扩展和截断数字

    上一篇博客我们讲解了计算机中整数的表示,包括无符号编码和补码编码,以及它们之间的互相转换,个人觉得那是非常重要的知识要点.这篇博客我们将介绍C语言中的有符号数和无符号数以及扩展和截断数字. 1.C语言 ...

  9. css预处理语言--让你的css编写更加简单方便

    CSS预处理语言之一-------LESS Less 是一门 CSS 预处理语言,它扩展了 CSS 语言,增加了变量.Mixin.函数等特性,使 CSS 更易维护和扩展. Less 可以运行在 Nod ...

  10. 基于 Electron 的爬虫框架 Nightmare

    作者:William 本文为原创文章,转载请注明作者及出处 Electron 可以让你使用纯 JavaScript 调用 Chrome 丰富的原生的接口来创造桌面应用.你可以把它看作一个专注于桌面应用 ...