拓扑排序 leetCode207题(dfs)

7 篇文章 0 订阅
7 篇文章 0 订阅

leetCode 207 题,简单易懂
图论算法拓扑排序

leetCode207 题

网上的答案算的挺快的,这个题想了有 5个小时,幸好是放假,有时间折腾

在这里插入图片描述题目思路
使用深度优先搜索

在这里插入图片描述

我们可以看到,第一幅图测试用例是 true,而第二幅图测试用例是false,我们可以建立一个有向图来理解这个题目,只要这个有向图中出现了环,那么就永远没有谁先谁后这个问题了,看过题目的人都知道,比如我要选修两门课程,如果我要修 1课程,那么先修课程是0 必须向学 0 课程 才能学 1号课程

而题目意思也很明显,就是要你推出矛盾,如果你学过图论的话,我们就可以把这个问题转化成 图论算法里面的拓扑排序,具体是什么就不讲了

  • 这里有很多解法,我使用的是 dfs
    举个例子: 数据告诉我要学算法,首先学数据结构,要学数据结构首先学C,要学C,首先学数学,结果有人说要学数学,首先学数据结构 ,结果你发现重新回到起点了

我们拿到一个数据,首先要建立一个节点之间的关系表, 这个表可以是临接矩阵表示,也可以是一个临接链表,对于稠密图来说最后使用临接矩阵,对于稀疏图来说最好使用临接链表,各有各的优点,这里使用了 临接矩阵的方式 下图代码的 adjacencyTable 二维数组中 ,遍历每个节点,如果,如果两点之间连通设置为 true,这里其实是有向图,也可以这么做
然后建立一个枚举数组, 枚举类Flag中,visiting表示正在进行dfs操作(会的都懂),visited表示访问过的,一定是有答案的,即无法成环,重点就在这里了,dfs递归的时候如果图没有环的话,到终点就会自然停止,如果这个图有环的话,在递归的时候我会重新回到起点,

代码如下图


// 枚举类,使用 枚举表示节点的状态
enum Flag{
    visiting,visited;
}



class Solution {
    public boolean canFinish(int c,int[][] course) {
        boolean [][] adjacencyTable = new boolean[c][c];
        Flag[] flags = new Flag[c];
        for(int [] row: course) {
            adjacencyTable[row[1]][row[0]] = true;

         }
         for(int i=0;i<c;i++) {
            if(!dfs(adjacencyTable,flags,i)) {
                return false;
            }
         }
         return true;
    }
    private boolean dfs(boolean[][] table,Flag [] flags,int curIndex) {
        if(flags[curIndex]==Flag.visiting)
            return false;
        if(flags[curIndex]==Flag.visited)
            return true;
        flags[curIndex] = Flag.visiting;
        for(int to=0;to<table.length;to++) {
            //如果 是环的话,返回 false,表示不行
            if(table[curIndex][to]&&!dfs(table,flags,to))
                return false;

        }
        //如果没找到环,就会标记为 visited
        flags[curIndex] = Flag.visited;//标记已经访问过了
        return true;


    }

}




这个思维的难点就是,你在递归下一层的时候你首先额做一个记号 ,表示我正在访问当前点(visiting),如果你在寻路的时候回到了起点,说明了你走进了 一个循环,这时候你如果再继续走的话就会进入一个死循环,因此显式的递归出口是: 当你访问到重复的节点是 立刻返回,因为已经确定了这是一个环 ,而自然结束条件时递归正常终止,这时隐式出口

刚才是用的临接矩阵,以下是临接表

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        //建立邻接表,因为题目中说不让用邻接矩阵
        LinkedList[] list=new LinkedList[numCourses];//所有节点
        for(int i=0;i<numCourses;i++){
            list[i]=new LinkedList<Integer>();//每个节点的后续节点
        }
        for(int i=0;i<prerequisites.length;i++){
            //因为数组后面一项依赖前面一项
            list[prerequisites[i][1]].add(prerequisites[i][0]);
        }
        boolean[] visit=new boolean[numCourses];
        for(int i=0;i<numCourses;i++){
            if(!dfs(list,visit,i)){//遍历i节点的后续节点 dfs为false表示忧患,进入if循环,返回false
                return false;
            }
        }
        return true;
    }
    //深度遍历,如果节点访问过,返回false 表示有环,不能访问
    //如果节点未访问过,将visit设置为true
    public boolean dfs(LinkedList[] list, boolean[] visit, int num){
        if(visit[num]){
            return false;
        }
        else{
            visit[num]=true;
        }
        for(int i=0;i<list[num].size();i++){//后续节点的dfs
            if(!dfs(list,visit,(int)list[num].get(i))){//后续节点i已经访问过
                return false;
            }
            list[num].remove(i);
        }
        visit[num]=false;
        return true;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值