LintCode 解题记录 17.10.14 拓扑排序
来源:互联网 发布:php 5.6 Oracle 编辑:程序博客网 时间:2024/06/06 05:22
第六周,抽了周二、周四和周六三晚做了五道题,来总结一下吧。
Clone Graph
题目描述
给你一幅图,还有一大堆Balabala关于图的介绍,然后返回这幅图的“克隆”。
思路
“克隆”是什么意思呢?就是对于你遍历到的每一个节点,用其节点的label值新建一个节点,然后对于其邻接节点也是如此的操作。此处遍历采用BFS的方法,需要一个OldNode->NewNode的一个map映射来实现。
代码
UndirectedGraphNode* cloneGraph(UndirectedGraphNode* node) { // write your code here if (node == NULL) return NULL; UndirectedGraphNode *copy; copy = new UndirectedGraphNode(node->label); //想到用这个映射题目思路就变得很清晰了 map<UndirectedGraphNode*, UndirectedGraphNode*> vis; queue<UndirectedGraphNode*> q; q.push(node); vis.insert({node, copy}); while (!q.empty()) { UndirectedGraphNode *front = q.front(); q.pop(); for (auto nextNode: front->neighbors) { if (vis.find(nextNode) == vis.end()) { copy = new UndirectedGraphNode(nextNode->label); vis[nextNode] = copy; q.push(nextNode); } //更新队首节点的复制节点的邻接链表的信息 vis[front]->neighbors.push_back(vis[nextNode]); } } return vis[node]; }
Course Schedule
题目描述
现在有n门课标号为0~n-1,给定一个pair{A, B},表示如果要修A课必须得先修B课,现在给你一个pair的集合,问你能否修完所有的课程。
思路
有向图判断是否有环问题。该方法常用拓扑排序来解决,而拓扑排序的常规写法为BFS。当然算法导论中介绍了拓扑排序的dfs写法,当然下面的题目中会涉及到。
代码
bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) { // write your code here vector<vector<int> > graph(numCourses, vector<int>(0)); //声明一个数组,表示图中每个节点的入度,就是有几个箭头指向该节点的意思 vector<int> InDegree(numCourses, 0); for (auto pair: prerequisites) { graph[pair.second].push_back(pair.first); InDegree[pair.first]++; } queue<int> q; for (int i = 0; i < numCourses; i++) { if (InDegree[i] == 0) q.push(i); } //拓扑排序的大致思路就是从入度为0的点开始,遍历其邻接链表,将其入度减1,相当于是删除该入度为0的节点,然后更新何其相邻的节点的入度信息,然后再次寻找下一个入度为0节点直到找不到。 while (!q.empty()) { int curr = q.front(); q.pop(); for (auto a: graph[curr]) { InDegree[a]--; if (InDegree[a] == 0) q.push(a); } } //最后判断所有节点的入度信息,如果有入度不为0的,就代表有环的存在。 for (int i = 0; i < numCourses; i++) { if (InDegree[i] != 0) return false; } return true; }
Course Schedule II
题目描述
跟Course Schedule一样,如果不能修完全部课程,就返回空数组,如果可以,就可以返回一个拓扑排序序列。
代码
vector<int> findOrder(int numCourses, vector<pair<int, int>> &prerequisites) { // write your code here vector<int> res; vector<vector<int>> graph(numCourses, vector<int>{}); vector<int> in(numCourses, 0); queue<int> q; for (auto pair: prerequisites) { graph[pair.second].push_back(pair.first); in[pair.first]++; } for (int i = 0; i < numCourses; i++) { if (in[i] == 0) q.push(i); } while (!q.empty()) { int curr = q.front(); q.pop(); res.push_back(curr); for (auto course: graph[curr]) { in[course]--; if (in[course] == 0) q.push(course); } } for (int i = 0; i < numCourses; i++) { if (in[i] != 0) return vector<int>{}; } return res; }
Route Between Two Nodes in Graph
题目描述
给定一幅有向图,和两个点,问你能否从一个点走到另一个点。
思路
显然是图的搜索问题,bfs+dfs。不过显然bfs的方法要更好一些。
代码
//Bfs Version,没用这个给的graph条件,可能这个条件是用来剪枝的(判断是否访问过)。 bool hasRoute(vector<DirectedGraphNode*> graph, DirectedGraphNode* s, DirectedGraphNode* t) { // write your code here queue<DirectedGraphNode*> q; q.push(s); while (!q.empty()) { DirectedGraphNode *curr = q.front(); q.pop(); if (curr->label == t->label) return true; for (auto node: curr->neighbors) { q.push(node); } } return false; }//Dfs Version bool hasRoute(vector<DirectedGraphNode*> graph, DirectedGraphNode* s, DirectedGraphNode* t) { // write your code here map<DirectedGraphNode*, int> vis; for (auto node: graph) vis[node] = 0; return dfs(s, t, vis); } bool dfs(DirectedGraphNode* s, DirectedGraphNode* t, map<DirectedGraphNode*, int> &vis) { if (s->label == t->label) return true; if (vis[s] == 1) return false; vis[s] = 1; for (auto node: s->neighbors) { if (dfs(node, t, vis)) return true; } return false; }
Topological Sorting
题目描述
给定有向图,返回拓扑排序序列。要求Bfs和Dfs两种实现。
思路1
Bfs实现。拓扑排序算法的常规思路:
1.从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它。
2.从网中删去该顶点,并且删去从该顶点发出的全部有向边。
3.重复上述两步,直到剩余的网中不再存在没有前驱的顶点为止。
因此对任一有向图进行拓扑排序有两种结果,一种是图中全部顶点都包含在拓扑序列中,这说明该图中不存在有向回路,另一种是图中部分顶点未被包含在拓扑序列中,这说明该图中存在有向回路。所以可以采用拓扑排序判断一个有向图中是否存在回路。
代码1
vector<DirectedGraphNode*> topSort(vector<DirectedGraphNode*>& graph) { // write your code here vector<DirectedGraphNode*> res; queue<DirectedGraphNode*> q; map<DirectedGraphNode*, int> in; //Initialization for (auto node: graph) in[node] = 0; for (auto node: graph) { for (auto next: node->neighbors) { in[next]++; } } for (auto pair: in) { if (pair.second == 0) q.push(pair.first); } while (!q.empty()) { DirectedGraphNode *curr = q.front(); q.pop(); res.push_back(curr); for (auto next: curr->neighbors) { in[next]--; if (in[next] == 0) q.push(next); } } return res; }
思路2
看了《算法导论》关于拓扑排序的一讲,是用Dfs实现的。顿时就感觉”还有这种操作??”。
拓扑排序的Dfs实现:(摘抄自算法导论)
TOPOLOGICAL-SORT(G)
1 call DFS(G) to compute finishing times v. for each vertex v
2 as each vertex is finished, insert in onto the front of a linked list.
3 return the linked list of vertices
意思就是说在dfs回溯的时候将当前结果插入到链表中(头插法),如果是按顺序插入到数组中那么最后得到的就是拓扑排序的逆序。
我们可以在O(V+E)的时间内完成拓扑排序,因为深度优先搜索算法的运行时间为O(V+E),将节点插入到链表最前端所需要的时间为O(1),一共只有|V|个节点需要插入。
代码2
vector<DirectedGraphNode*> topSort(vector<DirectedGraphNode*>& graph) { // DFS version vector<DirectedGraphNode*> res; map<DirectedGraphNode*, int> vis; map<DirectedGraphNode*, int> in; for (auto node: graph) { vis[node] = 0; in[node] = 0; } for (auto node: graph) { for (next: node->neighbors) { in[next]++; } } for (auto pair: in) { if (pair.second == 0) { dfs(vis, pair.first, res); } } reverse(res.begin(), res.end()); return res; } void dfs(map<DirectedGraphNode*, int> &vis, DirectedGraphNode *curr, vector<DirectedGraphNode*> &res) { for (auto next: curr->neighbors) { if (vis[next] == 1) continue; vis[next] = 1; dfs(vis, next, res); } //搜索结束之后,将该节点压入res中,最终的拓扑次序与节点的搜索完成时间恰好相反。 res.push_back(curr); }
Tips
如果在Dfs算法中加上判断有无环路,如何判断呢?只需要令vis[curr] = -1表示正在访问,如果访问到当前元素vis已经被置为-1,那说明存在环路,返回false即可。
贴一个别人写的拓扑排序dfs的Blog:
http://blog.csdn.net/acceptedxukai/article/details/6959882
- LintCode 解题记录 17.10.14 拓扑排序
- LintCode 解题记录17.10.21
- lintcode-拓扑排序-127
- LintCode-拓扑排序
- LintCode 127-拓扑排序
- lintcode(127)拓扑排序
- 拓扑排序-LintCode
- Lintcode 拓扑排序
- LintCode 解题记录 17.10.5 递归
- 拓扑排序 解题思路
- LintCode 解题记录17.4.27
- LintCode解题记录17.4.28
- LintCode解题记录 17.5.3
- LintCode 解题记录 2017.6.3
- LintCode 解题记录 7.11 ~ 7.16
- LintCode 解题记录 Matrix专题
- LintCode解题记录17.9.9
- LintCode解题记录-Catalan Number
- Ubuntu 14.04安装openssh7.5
- sensor_msgs/LaserScan Message
- 数组奇数排序
- vue安装
- node的用法
- LintCode 解题记录 17.10.14 拓扑排序
- 单点登录
- node.js安装使用
- Java实现给定年份和月份,输出该月的天数
- CentOS_6.5 x86_64系统安装及配置
- leetcode练习
- 2017.10.16以前的学习(3)
- 2frangment 切换变颜色
- 几种经典密码(classic cipher)