TopoSort 实现之不用高端静态链栈算法

和fawks大神探讨之后,理解了大神的思维方式,收获不少。感觉当年DS学的静态链栈实在坑爹,而且麻烦,好像和我的本质没有太大的区别哦。时间复杂度O(n+e)其实和O(n^2)是邻接矩阵和临界表的区别。
我的理解就是其实好像循环N次,{每次先找任意一个入度为0的点,然后从图中删除}。思路非常简单,但是coding实现还是有很多变化,我已开始不用indegree,直接matrix写了
一个O(n^3)的,就是不断在matrix上折腾,用了indegree数组之后,可以降到O(n^2)。这里需要注意的是,用什么样的DS可以时间复杂度尽可能小,如使用array,那么需要找值为0的数N次,同时还要找到这个点指向的几个点集,
然后对应indegree分量减一,看似sort,其实不需要,因为只需要找为0的,不需要后面的都有序,如果最小的如果不为0就不能TopoSort了,用sort会复杂度从内层O(n)到O(nlogn)上。好像heap可以O(logn),但是heap会打乱顺序,使得从matrix上找对应点在indegree上的index变得麻烦,因为还要高效的
随机访问 graph[top][j]==true, j的indegree--,所以还是数组方便,遍历一遍,indgree--的同时,再找=0的。


代码的算法过程中遇到一个bug,就是内层循环找graph[top][j]==true的,但是后面top又可能立马被赋值,使得每次找的未必是一个点的临界点,我看代码愣是没看出来,debug才得知,而且new的数组查看变量值又蛋疼。
所以感觉bug都出在左值上,以后查看奇怪的bug都去看左值,顶住循环的左值,一般循环都容易出问题,因为会不断操作。


代码风格上,感谢fawks大神给出的建议,变量用了全局确实很简单,不用new来new去,不用担心忘delete。然后还要判断bool值不用完整判断语句。
同时在探讨过程中熟悉了下二维数组传递的问题,new的数组可以用bool**,栈的数组用bool a[][b] 不能Bool* a[]这种,而且不能bool a[][]这也必须制定第二维长度,一直费解,后来从二维的最终实现上理解了,二维a[i][j] 本质是a[i*n+j] 这个是编译器做的事情,本质是一维连续数组,而n正是第二维长度,试想不知道n编译器怎么定位数据,第一维无所谓,编译器才不管你越不越界勒。所以到了更高位也可以根据这个知道那几位必须制定了,例如三维数组,看成很多[][] 第一维表示第几个[][]  所以a[i][j][k]=i*(d2*d3)+j*d3+k,因此必须指定d2 d3的维度,d1可以忽略,总结好像只有第一维可以忽略,不论多少维的。

附上fawks大神查阅后修改过代码风格的代码:
#include <iostream>
using namespace std;
const int MAXNUM=10000;
bool graph[MAXNUM][MAXNUM];
int indegree[MAXNUM];
void TopoSort(int n)
{
	//initial indegree
	int top=-1;
	for(int j=0;j<n;j++)
	{
		int indegreesum=0;
		for(int i=0;i<n;i++)
		{
			if(graph[i][j])
				indegreesum++;
		}
		if(indegreesum==0)
			top=j;
		indegree[j]=indegreesum;
	}
	if(top==-1){cout<<"No Topo Sort"<<endl;return;}


	for(int i=0;i<n;i++)
	{
		cout<<top<<" ";
		indegree[top]=-1;//-1 means has been output already
		if(i==n-1) break;//output n vertexes, finish
		int lastoutput=top;
		for(int j=0;j<n;j++)
		{
			if(indegree[j]==-1)continue;//has been output
			if(graph[lastoutput][j])//indegree==0, can not be true
			{
				indegree[j]--;
			}
			if(indegree[j]==0)//find any one indegree[j]==0
				top=j;
		}
		if(top==lastoutput)
		{
			cout<<"No Topo Sort"<<endl;
			break;
		}


	}
}
int main()
{
	int n;
	//const int m=6;
	while(cin>>n)
	{
		
		
		/*
		bool** graph=new bool*[n];
		for(int i=0;i<n;i++)
			graph[i]=new bool[n];
		//*/
	//	int* indegree=new int[n];
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<n;j++)
			{
				cin>>graph[i][j];
			}
		}
		TopoSort(graph, indegree, n);
		
		/*
		delete[] indegree;
		for(int i=0;i<n;i++)
			delete[] graph[i];
		delete[] graph;*/
	}
	return 0;
}

最后其实有一点总结,就是一定要请思考。因为原来DS学的后来看看很费解,还要一遍一遍手动模拟。其实自己想想,就是一个array,但是和书本的殊途同归。然后看书本的就好多了,这也印证了张一博大神”代码只有作者和上帝知道在干嘛“的说法

后来看到别处可以用DFS实现TS,而且教材PPT上也是这么说DFS重要性的,于是乎DFS搞搞TS,有一个问题,如果递归进去,发现找不到度为0,也即不存在TS时,可能需要另外输出count或者什么的来判断是否无TS,因为中间是不好print NO TS的。之前也是先建立Indegree,中间遍历的”剪枝“也是一样判断Indegree,同样用-1表示已经print

void dfs(int i, int n)
{
	cout<<i<<" ";
	indegree[i]=-1;
	for(int w=0;w<n;w++)//matrix: n loop, list: traverse linklist, max n 
	{
		//ajaacnet indegree--
		if(graph[i][w]==true)//w is i's adjanct vertex
		{
			if(indegree[w]!=-1)//has been output, can not be indegree 0, as graph[i][w] is true
			{
				indegree[w]--;
			}
		}
		
		//dfs
		if(indegree[w]==0)//
		{
			dfs(w,n);//matrix add if not -1, list in for point not null
		}
	}
}

所以遍历部分,其实都可以用DFS或者BFS实现。

和曹旻老师讨论后,有了更透彻的理解。如果用邻接表的话,是有区别的,静态链栈只要访问刚才print的点对应的边链表,因为top记录了当前indegree为0链接成的一个静态链,如果用邻接矩阵的话,则是遍历N个顶点,所以和用普通的数组实现是差不多的。


但其实时间复杂度在用邻接矩阵的时候有降低,数组还是O(n^2), 因为遍历数组找0,静态恋战 O(n+e) 因为是访问顶点的linklist (注:原来的说法:“只是说常数因子减少了, 就好比多个顶点对的最短路径用FLoyd而不用N次Dijstr a一样。”是有误的,我后来想清楚了)但是matrix 两者都一样的。

另外一个数组两用是说如果要用  把入度为0的点链接起来的方式   实现,比单独再开一个栈要节省空间

今天看到曹博的DPPPT里写DP算法过程是拓扑排序,很有意思,感觉是对DP算法的理解

顺便补上一个超赞,精简的典型基于DS的算法,附上http://cyukang.com/2013/11/20/topological-sort.html#comment-1458797806
用栈来模拟出拓扑的先后关系,代码看得让人爽到爆!
PS:代码要GCC编译器,某软的编译器不认识数组长度未知的数组定义,即使是在类的成员函数里!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值