关于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[10000010];
int h[10000010],low[10000010],z[10000010],wh=0,cont=0;
char flag[10000010];
int Anemone(int x)
{
    int no,mem;
    wh++;
    cont++;
    z[cont]=ne[x].num;
    flag[ne[x].num]=2;
    low[ne[x].num]=wh;
    no=h[ne[x].num];
    for(;;)
    {
        if(no==0)
        {
            break;
        }
        if(flag[ne[no].num]==1)
        {
            mem=Anemone(no);
            if(mem<low[ne[x].num])
            {
                low[ne[x].num]=mem;
                flag[ne[x].num]=0;
            }
        }
        else if(flag[ne[no].num]==2||flag[ne[no].num]==0)
        {
            if(low[ne[no].num]<low[ne[x].num])
            {
                low[ne[x].num]=low[ne[no].num];
                flag[ne[x].num]=0;
            }
        }
        no=ne[no].nex;
    }
    if(flag[ne[x].num]==2)
    {
        for(;;)
        {
            printf("%d ",z[cont]);
            if(flag[z[cont]]==2)
            {
                flag[z[cont]]=3;
                cont--;
                break;
            }
            else
            {
                flag[z[cont]]=3;
                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++)
    {
        flag[i]=1;
    }
    for(i=1;i<=n;i++)
    {
        if(flag[i]==1)
        {
            wh++;
            cont++;
            z[cont]=i;
            flag[i]=2;
            low[i]=wh;
            no=h[i];
            for(;;)
            {
                if(no==0)
                {
                    break;
                }
                if(flag[ne[no].num]==1)
                {
                    mem=Anemone(no);
                    if(mem<low[i])
                    {
                        low[i]=mem;
                        flag[i]=0;
                    }
                }
                else if(flag[ne[no].num]==2||flag[ne[no].num]==0)
                {
                    if(low[ne[no].num]<low[i])
                    {
                        low[i]=low[ne[no].num];
                        flag[i]=0;
                    }
                }
                no=ne[no].nex;
            }
            if(flag[i]==2)
            {
                for(;;)
                {
                    printf("%d ",z[cont]);
                    if(flag[z[cont]]==2)
                    {
                        flag[z[cont]]=3;
                        cont--;
                        break;
                    }
                    else
                    {
                        flag[z[cont]]=3;
                        cont--;
                    }
                }
                printf("\n");
            }
        }
    }
    return 0;
}

 

posted @ 2017-10-24 17:49  YangQuijote  阅读(585)  评论(1编辑  收藏  举报