从给定的依赖关系中查找是否有可能完成所有任务
您必须选择总共 n 个任务,标记为从 0 到 n-1。有些任务可能有先决条件,例如要选择任务 0,您必须先选择任务 1,它表示为一对:[0, 1]
给定任务总数和先决条件对列表,您是否有可能完成所有任务?
例子:
Input: 2, [[1, 0]]
Output: true
Explanation: There are a total of 2 tasks to pick. To pick task 1 you should have finished task 0. So it is possible.
Input: 2, [[1, 0], [0, 1]]
Output: false
Explanation: There are a total of 2 tasks to pick. To pick task 1 you should have finished task 0, and to pick task 0 you should also have finished task 1. So it is impossible.
Input: 3, [[1, 0], [2, 1], [3, 2]]
Output: true
Explanation: There are a total of 3 tasks to pick. To pick tasks 1 you should have finished task 0, and to pick task 2 you should have finished task 1 and to pick task 3 you should have finished task 2. So it is possible.
问:谷歌、推特、亚马逊和更多公司。
解:我们可以把这个问题看成一个图(与拓扑排序有关)问题。所有任务都是图的节点,如果任务 u 是任务 v 的先决条件,我们将添加从节点 u 到节点 v 的有向边。现在,这个问题相当于在先决条件表示的图中检测一个循环。如果图中存在循环,则不可能完成所有任务(因为在这种情况下,任务没有任何拓扑顺序)。 BFS 和 DFS 都可以用来解决它。
由于pair对于图算法的实现不方便,我们先将其转化为图。如果任务 u 是任务 v 的先决条件,我们将添加一条从节点 u 到节点 v 的有向边。
先决条件:检测有向图中的循环
使用DFS对于 DFS,它将首先访问一个节点,然后访问它的一个邻居,然后再访问这个邻居的一个邻居……以此类推。如果遇到当前DFS访问过程中访问过的节点,则检测到一个循环,返回false。否则它将从另一个未访问的节点开始并重复此过程,直到所有节点都已访问。注意要做好两条记录:一是记录所有访问过的节点,二是记录当前DFS访问中访问过的节点。
代码如下。我们使用一个向量visited 来记录所有被访问的节点,另一个向量onpath 来记录当前DFS 访问的被访问节点。当前访问完成后,我们将起始节点的 onpath 值重置为 false。
CPP
// CPP program to check whether we can finish all
// tasks or not from given dependencies.
#include
using namespace std;
// Returns adjacency list representation from a list
// of pairs.
vector > make_graph(int numTasks,
vector >& prerequisites)
{
vector > graph(numTasks);
for (auto pre : prerequisites)
graph[pre.second].insert(pre.first);
return graph;
}
// A DFS based function to check if there is a cycle
// in the directed graph.
bool dfs_cycle(vector >& graph, int node,
vector& onpath, vector& visited)
{
if (visited[node])
return false;
onpath[node] = visited[node] = true;
for (int neigh : graph[node])
if (onpath[neigh] || dfs_cycle(graph, neigh, onpath, visited))
return true;
return onpath[node] = false;
}
// Main function to check whether possible to finish all tasks or not
bool canFinish(int numTasks, vector >& prerequisites)
{
vector > graph = make_graph(numTasks, prerequisites);
vector onpath(numTasks, false), visited(numTasks, false);
for (int i = 0; i < numTasks; i++)
if (!visited[i] && dfs_cycle(graph, i, onpath, visited))
return false;
return true;
}
int main()
{
int numTasks = 4;
vector > prerequisites;
// for prerequisites: [[1, 0], [2, 1], [3, 2]]
prerequisites.push_back(make_pair(1, 0));
prerequisites.push_back(make_pair(2, 1));
prerequisites.push_back(make_pair(3, 2));
if (canFinish(numTasks, prerequisites)) {
cout << "Possible to finish all tasks";
}
else {
cout << "Impossible to finish all tasks";
}
return 0;
}
Java
// Java program to check whether we can finish all
// tasks or not from given dependencies.
import java.util.*;
public class GFG{
// class to store dependencies as a pair
static class pair{
int first, second;
pair(int first, int second){
this.first = first;
this.second = second;
}
}
// Returns adjacency list representation from a list
// of pairs.
static ArrayList> make_graph(int numTasks,
Vector prerequisites)
{
ArrayList> graph = new ArrayList>(numTasks);
for(int i=0; i());
}
for (pair pre : prerequisites)
graph.get(pre.second).add(pre.first);
return graph;
}
// A DFS based function to check if there is a cycle
// in the directed graph.
static boolean dfs_cycle(ArrayList> graph, int node,
boolean onpath[], boolean visited[])
{
if (visited[node])
return false;
onpath[node] = visited[node] = true;
for (int neigh : graph.get(node))
if (onpath[neigh] || dfs_cycle(graph, neigh, onpath, visited))
return true;
return onpath[node] = false;
}
// Main function to check whether possible to finish all tasks or not
static boolean canFinish(int numTasks, Vector prerequisites)
{
ArrayList> graph = make_graph(numTasks, prerequisites);
boolean onpath[] = new boolean[numTasks];
boolean visited[] = new boolean[numTasks];
for (int i = 0; i < numTasks; i++)
if (!visited[i] && dfs_cycle(graph, i, onpath, visited))
return false;
return true;
}
public static void main(String args[])
{
int numTasks = 4;
Vector prerequisites = new Vector();;
// for prerequisites: [[1, 0], [2, 1], [3, 2]]
prerequisites.add(new pair(1, 0));
prerequisites.add(new pair(2, 1));
prerequisites.add(new pair(3, 2));
if (canFinish(numTasks, prerequisites)) {
System.out.println("Possible to finish all tasks");
}
else {
System.out.println("Impossible to finish all tasks");
}
}
}
// This code is contributed by adityapande88.
CPP
// A BFS based solution to check if we can finish
// all tasks or not. This solution is mainly based
// on Kahn's algorithm.
#include
using namespace std;
// Returns adjacency list representation from a list
// of pairs.
vector > make_graph(int numTasks,
vector >& prerequisites)
{
vector > graph(numTasks);
for (auto pre : prerequisites)
graph[pre.second].insert(pre.first);
return graph;
}
// Finds in-degree of every vertex
vector compute_indegree(vector >& graph)
{
vector degrees(graph.size(), 0);
for (auto neighbors : graph)
for (int neigh : neighbors)
degrees[neigh]++;
return degrees;
}
// Main function to check whether possible to finish all tasks or not
bool canFinish(int numTasks, vector >& prerequisites)
{
vector > graph = make_graph(numTasks, prerequisites);
vector degrees = compute_indegree(graph);
for (int i = 0; i < numTasks; i++) {
int j = 0;
for (; j < numTasks; j++)
if (!degrees[j])
break;
if (j == numTasks)
return false;
degrees[j] = -1;
for (int neigh : graph[j])
degrees[neigh]--;
}
return true;
}
int main()
{
int numTasks = 4;
vector > prerequisites;
prerequisites.push_back(make_pair(1, 0));
prerequisites.push_back(make_pair(2, 1));
prerequisites.push_back(make_pair(3, 2));
if (canFinish(numTasks, prerequisites)) {
cout << "Possible to finish all tasks";
}
else {
cout << "Impossible to finish all tasks";
}
return 0;
}
Java
// A BFS based solution to check if we can finish
// all tasks or not. This solution is mainly based
// on Kahn's algorithm.
import java.util.*;
public class GFG{
// class to store dependencies as a pair
static class pair{
int first, second;
pair(int first, int second){
this.first = first;
this.second = second;
}
}
// Returns adjacency list representation from a list
// of pairs.
static ArrayList> make_graph(int numTasks,
Vector prerequisites)
{
ArrayList> graph = new ArrayList>(numTasks);
for(int i=0; i());
}
for (pair pre : prerequisites)
graph.get(pre.second).add(pre.first);
return graph;
}
// Finds in-degree of every vertex
static int[] compute_indegree(ArrayList> graph)
{
int degrees[] = new int[graph.size()];
for (ArrayList neighbors : graph)
for (int neigh : neighbors)
degrees[neigh]++;
return degrees;
}
// Main function to check whether possible to finish all tasks or not
static boolean canFinish(int numTasks, Vector prerequisites)
{
ArrayList> graph = make_graph(numTasks, prerequisites);
int degrees[] = compute_indegree(graph);
for (int i = 0; i < numTasks; i++) {
int j = 0;
for (; j < numTasks; j++)
if (degrees[j] == 0)
break;
if (j == numTasks)
return false;
degrees[j] = -1;
for (int neigh : graph.get(j))
degrees[neigh]--;
}
return true;
}
public static void main(String args[])
{
int numTasks = 4;
Vector prerequisites = new Vector();
prerequisites.add(new pair(1, 0));
prerequisites.add(new pair(2, 1));
prerequisites.add(new pair(3, 2));
if (canFinish(numTasks, prerequisites)) {
System.out.println("Possible to finish all tasks");
}
else {
System.out.println("Impossible to finish all tasks");
}
}
}
// This code is contributed by adityapande88.
Java
/*
* A union-find based solution for stating whether
* the given set of tasks with respective prequisites
* can be finished completely or not.
*/
import java.io.*;
import java.util.*;
//Driver code
class GFG {
public static void main(String args[]) throws IOException
{
int prerequisites[][] = new int[][]{ {1,0}, {2,1}, {3,2} };
Solution ob = new Solution();
if(ob.isPossible(4, prerequisites))
{
System.out.println("Yes");
}
else{
System.out.println("No");
}
}
}
//Driver Code Ends
//Solution code
class Solution {
//returns true/false stating whether tasks can be finished or not
public boolean isPossible(int N, int[][] prerequisites)
{
//make object of Union class for N tasks
Union u = new Union(N);
//traverse through pre-requisites array
for(int i = 0; i < prerequisites.length; i++){
//check whether given pre-requisite pair
//already have a common pre-requisite(parent)
if(u.findParent(prerequisites[i][0]) ==
u.findParent(prerequisites[i][1])) {
//tasks cannot be completed because there was
//a cyclic condition in the tasks
return false;
}
//make parent-child relation between pre-requisite task
//and the task dependent on it
u.makeParent(prerequisites[i][0], prerequisites[i][1]);
}
//if there was no cycle found, tasks can be completed
return true;
}
}
class Union {
//to store the parents of respective tasks
int[] arr;
//parameterised constructor for Union class
public Union(int n){
arr = new int[n];
//Initially, everyone is their own child
for(int i = 0; i < n; i++){
arr[i] = i;
}
}
public void makeParent(int a, int b){
//find parent of b and make it a's parent
arr[a] = findParent(b);
}
public int findParent(int c){
//when an independent task is found
if(c == arr)
return c;
//recursively find the parent of given task
return findParent(arr);
}
}
Possible to finish all tasks
使用BFS
BFS可以利用拓扑排序的思想来解决它。如果可以进行拓扑排序,则意味着没有循环,可以完成所有任务。
BFS 使用每个节点的入度。我们将首先尝试找到一个度数为 0 的节点。如果我们不这样做,则图中一定有一个循环,我们返回 false。否则我们找到了一个。我们将其入度设置为 -1 以防止再次访问它并将其所有邻居的入度减少 1。此过程将重复 n(节点数)次。如果我们没有返回 false,我们将返回 true。
CPP
// A BFS based solution to check if we can finish
// all tasks or not. This solution is mainly based
// on Kahn's algorithm.
#include
using namespace std;
// Returns adjacency list representation from a list
// of pairs.
vector > make_graph(int numTasks,
vector >& prerequisites)
{
vector > graph(numTasks);
for (auto pre : prerequisites)
graph[pre.second].insert(pre.first);
return graph;
}
// Finds in-degree of every vertex
vector compute_indegree(vector >& graph)
{
vector degrees(graph.size(), 0);
for (auto neighbors : graph)
for (int neigh : neighbors)
degrees[neigh]++;
return degrees;
}
// Main function to check whether possible to finish all tasks or not
bool canFinish(int numTasks, vector >& prerequisites)
{
vector > graph = make_graph(numTasks, prerequisites);
vector degrees = compute_indegree(graph);
for (int i = 0; i < numTasks; i++) {
int j = 0;
for (; j < numTasks; j++)
if (!degrees[j])
break;
if (j == numTasks)
return false;
degrees[j] = -1;
for (int neigh : graph[j])
degrees[neigh]--;
}
return true;
}
int main()
{
int numTasks = 4;
vector > prerequisites;
prerequisites.push_back(make_pair(1, 0));
prerequisites.push_back(make_pair(2, 1));
prerequisites.push_back(make_pair(3, 2));
if (canFinish(numTasks, prerequisites)) {
cout << "Possible to finish all tasks";
}
else {
cout << "Impossible to finish all tasks";
}
return 0;
}
Java
// A BFS based solution to check if we can finish
// all tasks or not. This solution is mainly based
// on Kahn's algorithm.
import java.util.*;
public class GFG{
// class to store dependencies as a pair
static class pair{
int first, second;
pair(int first, int second){
this.first = first;
this.second = second;
}
}
// Returns adjacency list representation from a list
// of pairs.
static ArrayList> make_graph(int numTasks,
Vector prerequisites)
{
ArrayList> graph = new ArrayList>(numTasks);
for(int i=0; i());
}
for (pair pre : prerequisites)
graph.get(pre.second).add(pre.first);
return graph;
}
// Finds in-degree of every vertex
static int[] compute_indegree(ArrayList> graph)
{
int degrees[] = new int[graph.size()];
for (ArrayList neighbors : graph)
for (int neigh : neighbors)
degrees[neigh]++;
return degrees;
}
// Main function to check whether possible to finish all tasks or not
static boolean canFinish(int numTasks, Vector prerequisites)
{
ArrayList> graph = make_graph(numTasks, prerequisites);
int degrees[] = compute_indegree(graph);
for (int i = 0; i < numTasks; i++) {
int j = 0;
for (; j < numTasks; j++)
if (degrees[j] == 0)
break;
if (j == numTasks)
return false;
degrees[j] = -1;
for (int neigh : graph.get(j))
degrees[neigh]--;
}
return true;
}
public static void main(String args[])
{
int numTasks = 4;
Vector prerequisites = new Vector();
prerequisites.add(new pair(1, 0));
prerequisites.add(new pair(2, 1));
prerequisites.add(new pair(3, 2));
if (canFinish(numTasks, prerequisites)) {
System.out.println("Possible to finish all tasks");
}
else {
System.out.println("Impossible to finish all tasks");
}
}
}
// This code is contributed by adityapande88.
Possible to finish all tasks
参考:
https://leetcode.com/problems/course-schedule/
使用联合查找
另一种方法是Union-find方法也可以用来解决这个问题。我们得到的每一对都可以被认为是父子关系。
一旦我们找到一对应该处于父子关系的任务,我们就会检查它们是否已经有一个共同的父级,因此这意味着任务中存在一个永远不会结束的依赖循环,因此解决了这样的问题一组任务是不可能的。
Java
/*
* A union-find based solution for stating whether
* the given set of tasks with respective prequisites
* can be finished completely or not.
*/
import java.io.*;
import java.util.*;
//Driver code
class GFG {
public static void main(String args[]) throws IOException
{
int prerequisites[][] = new int[][]{ {1,0}, {2,1}, {3,2} };
Solution ob = new Solution();
if(ob.isPossible(4, prerequisites))
{
System.out.println("Yes");
}
else{
System.out.println("No");
}
}
}
//Driver Code Ends
//Solution code
class Solution {
//returns true/false stating whether tasks can be finished or not
public boolean isPossible(int N, int[][] prerequisites)
{
//make object of Union class for N tasks
Union u = new Union(N);
//traverse through pre-requisites array
for(int i = 0; i < prerequisites.length; i++){
//check whether given pre-requisite pair
//already have a common pre-requisite(parent)
if(u.findParent(prerequisites[i][0]) ==
u.findParent(prerequisites[i][1])) {
//tasks cannot be completed because there was
//a cyclic condition in the tasks
return false;
}
//make parent-child relation between pre-requisite task
//and the task dependent on it
u.makeParent(prerequisites[i][0], prerequisites[i][1]);
}
//if there was no cycle found, tasks can be completed
return true;
}
}
class Union {
//to store the parents of respective tasks
int[] arr;
//parameterised constructor for Union class
public Union(int n){
arr = new int[n];
//Initially, everyone is their own child
for(int i = 0; i < n; i++){
arr[i] = i;
}
}
public void makeParent(int a, int b){
//find parent of b and make it a's parent
arr[a] = findParent(b);
}
public int findParent(int c){
//when an independent task is found
if(c == arr)
return c;
//recursively find the parent of given task
return findParent(arr);
}
}
Yes