LeetCode - 207. 课程表(回溯剪枝)

207. 课程表

你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]

给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?

在这里插入图片描述

解题思路: 本题的背景是拓扑排序,本质上可以转化为求图是否存在环的问题。图论中有几个经典的问题,比如求连通域个数、图中是否有环路、图的最短路问题等等,这些都可以化归到图的遍历问题,后续专门开一个专栏对图的经典问题进行讨论和总结。

本题的问题相当于转化为图中是否存在环的问题,网上应该有大量资料阐述环的问题,但是之前没有相关的积累,做此题有点卡克,现在把思路的路径记录下来。首先分析环的特点,我们在遍历的过程中,如果遇到环路,那么按照DFS方式进行遍历,后面遍历到的点会沿着环把前面遍历过的点又会遍历一次,抓住这个特点,我一开始想到的是用一个visited数组记录那些点被遍历过,另外还要考虑到图不连通的问题,如果把visited数组作为所有连通域的访问记录,下面这个case判环就会出问题,1->0,按照顺序会先访问0,再访问1的时,程序会理解成另一个连通域,0已经访问了,再次访问会被判断成环,如果每次用dfs判断一个连通域就新建一个visited,有会在下面这个case出问题,2->1,1->0,2->0,在沿着2->1->0路径遍历时会把0标记会访问过,在回溯到2->0路径上时,0被标记了又会判断成环,至此,我换了个思路在想,是否可以把把访问的路径用out数组记录下来,然后让这个out数组在访问的过程进行伸缩,换句话说递归进入的时候push_back,递归退出的时候pop_back,写完之后,突然发现,这不就是之前经常写的递归回溯算法嘛,回溯剪枝算法是图中非常经典的算法,可以有很多用处,比如这里的判断环,还有找寻符合要求的路径等等,回溯的过程中如果能够剪枝,尽量剪枝。OK,至此图的判环问题就容易解了。

下面我们简单讨论一下回溯剪枝算法的内涵,回溯剪枝算法,我一直把它理解成一个可伸可缩的路径,随着递归深度增加,路径尾部在增长,随着递归退出,路径尾巴在缩短,这就是为什么回溯剪枝算法能够用来判断环路问题,路径求和问题,因为这些问题都可以化归到路径特征问题,那么总结一句话,回溯剪枝算法能解决大部分路径特征问题,编码特点是维护一个路径数组,在递归时尾部追加访问节点,在回溯时尾部弹出节点

// DFS
// 判断图中是否有环,按照回溯剪枝算法写即可
// Time: O(n^2), Space:O(n^2)
class Solution {
public:
    void helper(int node, vector<vector<int>>& vex, vector<bool>& visited, bool& res) {
        if (visited[node]) {res = false; return;}
        visited[node] = true;
        for (auto &a : vex[node]) {
            helper(a, vex, visited, res);
            if (res == false) return;
        }
        visited[node] = false;
    }
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> vex(numCourses);
        for (auto &a : prerequisites) {
            vex[a[1]].push_back(a[0]);
        }
        bool res = true;
        for (int i = 0; i < numCourses; ++i) {
            vector<bool> visited(numCourses, false);
            helper(i, vex, visited, res);
            if (res == false) break;
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值