和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大神查阅后修改过代码风格的代码:
所以遍历部分,其实都可以用DFS或者BFS实现。
我的理解就是其实好像循环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实现。
和曹旻老师讨论后,有了更透彻的理解。如果用邻接表的话,是有区别的,
但其实时间复杂度在用邻接矩阵的时候有降低,数组还是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编译器,某软的编译器不认识数组长度未知的数组定义,即使是在类的成员函数里!