• 关于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("
    ");
    	}
    	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("
    ");
    			}
    		}
    	}
    	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("
    ");
        }
        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("
    ");
                }
            }
        }
        return 0;
    }
  • 相关阅读:
    vue+elementUI,前端导出表格,去除不需要的表格列
    Git撤销远端提交
    剖析 SPI 在 Spring 中的应用
    vivo大规模 Kubernetes 集群自动化运维实践
    如何在Vue项目中,通过点击DOM自动定位VScode中的代码行?
    vivo 容器集群监控系统架构与实践
    nginx rewrite
    nginx location
    IPVS
    得到 spring ApplicationContext
  • 原文地址:https://www.cnblogs.com/YanQuijote/p/7724883.html
Copyright © 2020-2023  润新知