图的算法题
题目就是求一堆课程有先修和后修的区别,这些课程能否构成拓扑排序而不矛盾;
应该算是我做的第一个关于图的算法题,一开始毫无思路,看了网上的sloutions,发现这道题还是比较简单的,可以用bfs和dfs都可以做。感觉还是比较典型的关于图算法例题。
这个题解讲的还是比较明白的,虽然leetcode的题解也很明白,但是是英文,我看了也是讲的差不多。
bfs:
BFS:典型的拓扑排序。原理也很简单,在一个有向图中,每次找到一个没有前驱节点的节点(也就是入度为0的节点), * 然后把它指向其他节点的边都去掉,重复这个过程(BFS),直到所有节点已被找到,或者没有符合条件的节点(如果图中有环存在)
1 vector<unordered_set<int>> makegraph(int numCourses, vector<pair<int, int>> prerequisites){ 2 vector<unordered_set<int>> graph(numCourses); 3 for(auto pre:prerequisites) graph[pre.second].insert(pre.first); 4 return graph; 5 } 6 vector<int> compute_degrees(vector<unordered_set<int>> graph){ 7 vector<int> degrees(graph.size(),0); 8 for(auto n:graph) 9 for(int neigh:n) 10 degrees[neigh]++; 11 return degrees; 12 } 13 bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) { 14 vector<unordered_set<int>> graph=makegraph(numCourses,prerequisites); 15 vector<int> degrees=compute_degrees(graph); 16 for(int i=0;i<numCourses;i++){ 17 int j=0; 18 for(j;j<numCourses;j++) 19 if(!degrees[j]) break; 20 if(j==numCourses) return false; 21 degrees[j]=-1; 22 for(int neigh:graph[j]) 23 degrees[neigh]--; 24 } 25 return true; 26 } 27 };
dfs
DFS的解法,也需要建立有向图,还是用二维数组来建立,和BFS不同的是,我们像现在需要一个一维数组visit来记录访问状态, * 大体思路是,先建立好有向图,然后从第一个门课开始,找其可构成哪门课,暂时将当前课程标记为已访问, * 然后对新得到的课程调用DFS递归,直到出现新的课程已经访问过了,则返回false,没有冲突的话返回true,然后把标记为已访问的课程改为未访问
1 //dfs 2 vector<unordered_set<int>> makegraph(int numCourses, vector<pair<int, int>>& prerequisites){ 3 vector<unordered_set<int>> graph(numCourses); 4 for(auto pre:prerequisites) 5 graph[pre.second].insert(pre.first); 6 return graph; 7 } 8 bool dfscycle(vector<unordered_set<int>>& graph,int i,vector<bool>& onpath,vector<bool>& visit){ 9 if(visit[i]) return false; 10 visit[i]=onpath[i]=true; 11 for(int neigh:graph[i]) 12 if(onpath[neigh]||dfscycle(graph,neigh,onpath,visit)) 13 return true; 14 return onpath[i]=false; 15 } 16 bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) { 17 vector<unordered_set<int>> graph=makegraph(numCourses,prerequisites); 18 vector<bool> visit(numCourses,false),onpath(numCourses,false); 19 for(int i=0;i<numCourses;i++) 20 if(!visit[i]&&dfscycle(graph,i,onpath,visit)) 21 return false; 22 return true; 23 }
在编写dfs code的时候,我发现一个很有趣的地方,之前在写函数的时候,如果不需要改变使用参数的值时,我尽量不用& 引用,担心不小心操作失误,改变了值,但是今天coding这道题的时候,由于建了一个图graph,我写函数的时候没用&,在输入量达到1000时,submission显示tle,当时我真是非常诧异,因为我跟solution写的大致相同,但我却tle了,后来查阅后发现,在函数时引用参数,是能提高程序效率的,尤其是当引用的参数非常大时,如一个图,用&提高程序效率,所以我上文中特地标了红色来提醒一下。
用引用肯定可以提高效率,不过要看情况,只有当引用的是占用“较大的内存空间的对象或结构体变量”时,才可能节约更多的时间。简单变量的引用不会节省空间。道理是这样的:
当实参是较大的对象或结构体变量时,如果直接传递给相应的函数形参,需要创建临时对象或结构本变量,而且还需要复制大块内存数据,这肯家比只传递一个“引用”(实际上是一种变形指针)要耗时得多。
如果你想测试的话,你应该构造“大点”的对象或结构体变量才行。
因为需要创建临时变量而拖累程序,而引用直接传递了地址。