1. 题目描述
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
示例 1:
输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
示例 2:
输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。
提示:
1 <= numCourses <= 105
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
prerequisites[i] 中的所有课程对 互不相同
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/course-schedule
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
2.代码如下
typedef struct st_Queue
{
int array[2000];
int head;
int tail;
int size;
}Queue;
#if 0
使用队列,动态地维护了结点的出入队动作代表的遍历与否。
#endif
void enqueue(Queue *queue,int val)
{
queue->array[queue->tail] = val;
queue->tail += 1;
queue->size += 1;
}
int dequeue(Queue *queue)
{
int tmp = queue->array[queue->head];
queue->head += 1;
queue->size -= 1;
return tmp;
}
void que_init(Queue *queue)
{
memset(queue,0,sizeof(Queue));
}
bool canFinish(int numCourses, int** prerequisites, int prerequisitesSize, int* prerequisitesColSize){
int i = 0,j = 0;
int *degree = NULL;
int **map = NULL;
int *res = NULL;
int tmpval = 0;
int cnt = 0;
Queue g_queue;
que_init(&g_queue);
degree = (int *)malloc(sizeof(int)*numCourses);
memset(degree,0,sizeof(int)*numCourses);
map = (int **)malloc(sizeof(int *)*numCourses);
memset(map,0,sizeof(int *)*numCourses);
for (i = 0;i < numCourses;i++)
{
map[i] = (int *)malloc(sizeof(int)*numCourses);
memset(map[i],0,sizeof(int)*numCourses);
}
for (j = 0; j < prerequisitesSize;j++)//使用degree记录每个课程的出入度
{
degree[prerequisites[j][0]] += 1;//入度
map[prerequisites[j][1]][prerequisites[j][0]] = 1;
}
for (j=0;j<numCourses;j++)
{
if (degree[j] == 0)
{
enqueue(&g_queue,j);
}
}
while (g_queue.size != 0)
{
tmpval = dequeue(&g_queue);
cnt++;
for (i = 0 ;i < numCourses;i++)
{
if (map[tmpval][i] == 1)//找到节点
{
degree[i] -= 1;//入度-1
if (degree[i] == 0)//如果入度为0,则入队
{
enqueue(&g_queue,i);
}
}
}
}
for (i = 0;i < numCourses;i++)
{
free(map[i]);
}
free(map);
free(degree);
if (cnt != numCourses)
{
return false;
}
return true;
}
c++实现
/*
* date: 2024-06-06
* city: hangzhou
* author: zhangyb
*/
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <memory>
using namespace std;
struct Node {
Node(int val)
: val_(val)
{
}
int val_;
std::set<shared_ptr<Node>> in;
std::set<shared_ptr<Node>> out;
};
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
std::map<int, std::shared_ptr<Node>> valToNode;
auto getNode = [&valToNode](int val) -> std::shared_ptr<Node>{
auto nodeIter = valToNode.find(val);
if (nodeIter != valToNode.end())
return nodeIter->second;
auto newNodePtr = std::shared_ptr<Node>(new Node(val));
valToNode.insert({val, newNodePtr});
return newNodePtr;
};
for (auto& prerequisite : prerequisites) {
auto selfNode = getNode(prerequisite[0]);
auto preNode = getNode(prerequisite[1]);
if (selfNode == preNode)
return false;
selfNode->out.insert(preNode);
preNode->in.insert(selfNode);
}
std::queue<std::shared_ptr<Node>> nodesWithZeroIn;
for (auto& [_, nodePtr] : valToNode) {
if (nodePtr->in.size() == 0)
nodesWithZeroIn.push(nodePtr);
}
while ((int)nodesWithZeroIn.size() > 0) {
auto nodePtr = nodesWithZeroIn.front();
nodesWithZeroIn.pop();
for (auto& outNodePtr : nodePtr->out) {
outNodePtr->in.erase(nodePtr);
if ((int)outNodePtr->in.size() == 0) {
nodesWithZeroIn.push(outNodePtr);
}
}
}
for (auto& [_, nodePtr] : valToNode) {
if (nodePtr->in.size() != 0)
return false;
}
return true;
}
};
为每个课程构建节点,设置出入度,最终从入度为0的节点入手,将他们一一从图中移除,移除,即,将它们指向的节点的入度节点集合in中删除该节点,并判断移除以后这些节点的入度是否为0,为0就将它们加入入度为0的节点队列中.
最终判断是否还有节点的入度不为0,不为0说明存在环,即循环依赖,则不满足.