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

原创粉丝点击