秋招笔试惨痛经历之——DFS

1. 子集类型(直接添加结果子集,从i或i+1开始遍历,不需要visited判断重复搜索):原数组所有可能的组合。因此要从i+1继续搜。不需要使用visited防止重复搜索(因为不是从0开始遍历)

输入: nums = [1,2,3]
输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

输入: [1,2,2]
输出:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]
  1. 有重复元素,先排序去重
class Solution {

    public List<List<Integer>> result = new ArrayList<>();
    public List<Integer> list = new ArrayList<>();

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        /*
        子集,有重复,要去重,先排序
        8.23
        */

        if(nums.length == 0){
            return result;
        }

        //有重复元素,解集不允许重复,先排序
        Arrays.sort(nums);

        dfs(nums,0);

        return result;
    }

    public void dfs(int[] nums,int index){
        //result直接添加list
        result.add(new ArrayList<>(list));

        for(int i=index;i<nums.length;i++){

            //去重
            if(i > index && nums[i] == nums[i-1]){
                continue;
            }

            list.add(nums[i]);

            dfs(nums,i+1);

            list.remove(list.size()-1);
        }
    }
}
  1. 无重复元素,直接进行dfs
class Solution {

    public ArrayList<List<Integer>> result = new ArrayList<>();
    public ArrayList<Integer> list = new ArrayList<>();

    public List<List<Integer>> subsets(int[] nums) {
        /*
        子集,无重复,index从0开始
        8.23
        */
        //dfs所有组合模板

        if(nums.length < 1){
            return result;
        }

        //索引从0开始
        dfs(nums,0);

        return result;
    }

    public void dfs(int[] nums,int index){

        result.add(new ArrayList<>(list));

        for(int i=index;i<nums.length;i++){
            
            //list直接添加当前nums[i]
            list.add(nums[i]);

            //继续添加下一个元素
            dfs(nums,i+1);

            //剪枝
            list.remove(list.size()-1);
        }
    }
}

2. 全排列(从0开始遍历,需要visited判断重复复搜索,结果子集长度与原数组长度一致):找出所有和原数组长度相同的组合。每次递归都从0开始搜索nums数组,需要visited数组判断重复搜索

输入: [1,2,3]
输出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

输入: [1,1,2]
输出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]
  1. 全排列:无重复元素,直接进行dfs
class Solution {

    public ArrayList<List<Integer>> result = new ArrayList<>();
    public ArrayList<Integer> list = new ArrayList<>();

    public List<List<Integer>> permute(int[] nums) {
        /*
        全排列,无重复。
        8.23
        */
        //因为全排列,每次都重新遍历数组,需要visited防止重复搜索

        if(nums.length == 0){
            return result;
        }

        boolean[] visited = new boolean[nums.length];

        dfs(nums,visited);

        return result;
    }

    public void dfs(int[] nums,boolean[] visited){
        //递归出口:当list长度等于nums长度时
        if(list.size() == nums.length){
            result.add(new ArrayList<>(list));
            return;
        }

        //全排列,长度和原数组一直,从0开始重新遍历,用visited来防止重复搜索
        for(int i=0;i<nums.length;i++){
            
            if(visited[i]){
                continue;
            }

            visited[i] = true;

            list.add(nums[i]);

            dfs(nums,visited);

            visited[i] = false;

            list.remove(list.size()-1);
        }
    }
}
  1. 全排列:有重复元素,先排序,在遍历过程中进行去重
class Solution {

    public List<List<Integer>> result = new ArrayList<>();
    public List<Integer> list = new ArrayList<>();

    public List<List<Integer>> permuteUnique(int[] nums) {
        /*
        全排列:有重复元素,先去重
        8.23
        */

        if(nums.length == 0){
            return result;
        }

        Arrays.sort(nums);
        boolean[] visited = new boolean[nums.length];

        dfs(nums,visited);

        return result;
    }

    public void dfs(int[] nums,boolean[] visited){
        //递归出口:当list长度和nums长度一致时
        if(nums.length == list.size()){
            result.add(new ArrayList<>(list));
            return;
        }

        for(int i=0;i<nums.length;i++){
            
            if(visited[i]){
                continue;
            }

            if(i > 0 && nums[i] == nums[i-1] && visited[i-1]){
                continue;
            }

            visited[i] = true;

            list.add(nums[i]);

            dfs(nums,visited);

            visited[i] = false;

            list.remove(list.size()-1);
        }
    }
}
  1. 字符串全排列:输入一个字符串,打印出该字符串中字符的所有排列。
输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]
class Solution {

    List<String> result = new ArrayList<>();

    public String[] permutation(String s) {
        /*
        字符串全排序:可能有重复字符串,先转换为字符数组,然后排序,进行去重。全排列,要用visited判断重复搜索
        8.23
        */
        
        char[] str = s.toCharArray();

        Arrays.sort(str);

        boolean[] visited = new boolean[str.length];

        //初始化temp为"",传入visited
        dfs(str,"",visited);

        //将List转为String数组
        return result.toArray(new String[result.size()]);
    }

    public void dfs(char[] str,String temp,boolean[] visited){
        //递归出口:当str.length() == temp.length()
        if(str.length == temp.length()){
            result.add(temp);
            return;
        }

        //全排列,从0开始
        for(int i=0;i<str.length;i++){
            if(visited[i]){
                continue;
            }

            //去重
            if(i > 0 && str[i] == str[i-1] && visited[i-1]){
                continue;
            }

            visited[i] = true;

            dfs(str,temp+str[i],visited);

            visited[i] = false;
        }
    }
}
  1. 电话号码的字母组合:给定一个
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
class Solution {


    ArrayList<String> result = new ArrayList<>();
    //使用HashMap存储电话数字和字符串
    HashMap<Character,String> map = new HashMap<>();
    String digits = "";

    public List<String> letterCombinations(String digits) {
        /*
        电话号码的字母组合,先用HashMap预存。递归时通过index提取当前字符串。全排列类型。遍历从0开始
        8.23
        */

        if(digits.length() == 0){
            return result;
        }

        //初始化map
        map.put('2', "abc");
        map.put('3', "def");
        map.put('4', "ghi");
        map.put('5', "jkl");
        map.put('6', "mno");
        map.put('7', "pqrs");
        map.put('8', "tuv");
        map.put('9', "wxyz");

        //初始化digits,用于作为key提取value
        this.digits = digits;

        //index为0,初始化temp为""
        dfs(0,"");

        return result;
    }

    public void dfs(int index,String temp){
        //递归出口,如果temp的长度等于digits的长度
        if(temp.length() == digits.length()){
            result.add(temp);
            return;
        }

        //提取当前digits的index对应value
        String str = map.get(digits.charAt(index));

        //遍历str,全排列,从0开始遍历
        for(int i=0;i<str.length();i++){
            
            //index+1,temp添加当前字符
            dfs(index+1,temp+str.charAt(i));
        }
    }
}

3. 组合总和(要递减,先排序,递减到0就添加,从i或i+1开始遍历):给定一个目标值,求数组元素所能组成的和为target的组合。每次递归遍历不用从0开始,target递减当前元素

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]
  1. 组合总和:无重复、元素可以使用无限次
class Solution {

    public ArrayList<List<Integer>> result = new ArrayList<>();
    public ArrayList<Integer> list = new ArrayList<>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        /*
        组合总和,
        1. 无重复,不需要去重。
        2. target要递减,先排序。
        3. 因为元素可以多次使用,因此遍历从i开始,所以不需要使用visited判断重复搜索。遍历过程先判断递减之后是否越界

        8.23
        */

        if(candidates.length == 0){
            return result;
        }

        Arrays.sort(candidates);
        dfs(candidates,0,target);

        return result;
    }

    public void dfs(int[] candidates,int index,int target){
        //递归出口
        if(target == 0){
            result.add(new ArrayList<>(list));
            return;
        }

        for(int i=index;i<candidates.length;i++){

            //防止溢出
            if(target - candidates[i] < 0){
                break;
            }

            list.add(candidates[i]);

            dfs(candidates,i,target-candidates[i]);

            list.remove(list.size()-1);
        }
    }
}
  1. 组合总和:有重复,元素只能使用一次
class Solution {
    
    public List<List<Integer>> result = new ArrayList<>();
    public List<Integer> list = new ArrayList<>();

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        /*
        组合总和:有重复元素,元素不允许使用多次
        8.23
        */

        if(candidates.length == 0){
            return result;
        }

        //target要递减并且有重复要去重
        Arrays.sort(candidates);

        dfs(candidates,target,0);

        return result;
    }

    public void dfs(int[] candidates,int target,int index){
        //递归出口
        if(target == 0){
            result.add(new ArrayList<>(list));
            return;
        }

        for(int i=index;i<candidates.length;i++){
            
            //防溢出
            if(target - candidates[i] < 0){
                break;
            }

            //去重
            if(i > index && candidates[i] == candidates[i-1]){
                continue;
            }

            list.add(candidates[i]);

            dfs(candidates,target-candidates[i],i+1);

            list.remove(list.size()-1);
        }   
    }
}
  1. 组合总和:找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
class Solution {

    public ArrayList<List<Integer>> result = new ArrayList<>();
    public ArrayList<Integer> list = new ArrayList<>();

    public List<List<Integer>> combinationSum3(int k, int n) {
        /*
        组合总和:和1、2同理,甚至更简单。遍历从1到9
        8.23
        */

        //n和k用于终止递归,当n递减i到0且k == list.size()时
        dfs(1,n,k);
        
        return result;
    }

    public void dfs(int start,int end,int k){
        //递归出口:当end递减到0且list.size() == k时
        if(end == 0 && list.size() == k){
            result.add(new ArrayList<>(list));
            return;
        }

        //1到9
        for(int i=start;i<=9;i++){

            list.add(i);
            
            dfs(i+1,end-i,k);

            list.remove(list.size()-1);
        }
    }
}
  1. 组合:给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
class Solution {

    public List<List<Integer>> result = new ArrayList<>();
    public List<Integer> list = new ArrayList<>();

    public List<List<Integer>> combine(int n, int k) {
        /*
        组合:1到n数组中长度为k的组合
        8.23
        */

        dfs(1,n,k);

        return result;
    }

    public void dfs(int start,int end,int k){
        //递归出口:当list.size() == k
        if(list.size() == k){
            result.add(new ArrayList<>(list));
            return;
        }

        for(int i=start;i<=end;i++){

            list.add(i);

            dfs(i+1,end,k);

            list.remove(list.size()-1);
        }
    }
}
  1. 目标和:在给定数组中找出可以通过加减使和为S的组合个数。逆向思维:找出可以通过加减使S为0的组合
class Solution {

    int result = 0;
    public int findTargetSumWays(int[] nums, int S) {
        /*
        目标和:非负数整数数组,要求能过通过加减得到S,求有多少种方法(逆向思维,求有多少种组合能够通过加和减使S为0)
        8.23
        */

        //index从0开始
        dfs(nums,S,0);

        return result;
    }

    public void dfs(int[] nums,int S,int index){
        //递归出口,当S == 0且index == nums.length
        if(S == 0 && index == nums.length){
            result++;
            return;
        }

        //如果不符合S == 0的要求
        if(index == nums.length){
            return;
        }

        //S + nums[index]
        dfs(nums,S+nums[index],index+1);

        //S - nums[index]
        dfs(nums,S-nums[index],index+1);
    }
}
  1. 24点游戏(B站笔试):类似目标和,只不过多了乘和除。除的时候要判断除数(nums[index])不能为0
Class Solution{
    static boolean result = true;
    private static boolean Game24(int[] nums) {
        
        if(nums.length == 0){
            return false;
        }

        dfs(nums,24,0);

        return result;
    }

    private static void dfs(int[] nums, int sum, int index) {

        //递归出口:如果sum == 0且index == nums.length
        if(sum == 0 && index == nums.length){
            result = true;
            return;
        }

        //如果不符合要求
        if(index == nums.length){
            result = false;
            return;
        }

        //加减乘除使24为0
        dfs(nums,sum+nums[index],index+1);
        dfs(nums,sum-nums[index],index+1);
        dfs(nums,sum*nums[index],index+1);

        if(nums[index] != 0){
            dfs(nums,sum/nums[index],index+1);
        }
    }
}
  1. 丢弃最少物品(网易笔试):分与不分
  2. 题目:给出n个物品,每个物品都有自己的价值,每个物品只有一件,这些物品需要分给两个人,要求分配完之后,两个人的物品价值相同。分配完成之后,会丢弃剩下的物品,求最少要丢弃多少物品。
输入
输入第一行为总的测试数据个数,第二行为物品个数n,第三行为n个物品的价值。
1
5
30 60 5 15 30
输出
20 丢弃5和15,把60分配给第一个人,2个30分配给第二个人。
  1. 使用dfs,分给A,分给B,都不分
import java.util.Scanner;

public class Solution{

    public static int result = Integer.MAX_VALUE;
    public static int sum = 0;
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);

        //用例数
        int group = sc.nextInt();

        while(group-- > 0){
            int len = sc.nextInt();

            int[] nums = new int[len];

            for(int i=0;i<nums.length;i++){
                nums[i] = sc.nextInt();

                //先计算sum,用于判断分得的两次结果是否相等
                sum += nums[i];
            }

            //index为0,第一个人拥有的为0,第二个人拥有的为0
            dfs(nums,0,0,0);

            System.out.println(result);
        }
    }

    private static void dfs(int[] nums, int index, int result1, int result2) {

        //递归出口
        if(index == nums.length){
            //判断result1 == result2
            if(result1 == result2){
                //取小的值,(result1和result2一样)
                result = Math.min(result,sum - 2*result1);
            }
            return;
        }

        //1. 谁也不给
        dfs(nums,index+1,result1,result2);

        //2. 给A
        dfs(nums,index+1,result1+nums[index],result2);

        //3. 给B
        dfs(nums,index+1,result1,result2+nums[index]);
    }
}
  1. 括号生成:数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
class Solution {

    ArrayList<String> result = new ArrayList<>();

    public List<String> generateParenthesis(int n) {
        /*
        括号生成
        7.22
        */
        //dfs,[n,n]中心扩散
        dfs(n,n,"");

        return result;
    }

    public void dfs(int left,int right,String temp){
        //递归出口
        if(left == 0 && right == 0){
            result.add(temp);
            return;
        }

        //如果left > 0,拼接左半
        if(left > 0){
            dfs(left-1,right,temp+"(");
        }
        
        //如果right > left,拼接右半
        if(right > left){
            dfs(left,right-1,temp+")");
        }
    }
}

4. 树路径

  1. 路径总和(路径数目):判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        /*
        判断是否存在到某个值的路径总和
        8.23
        */
        //要求从根节点开始,所以sum先递减root.val

        if(root == null){
            return false;
        }

        sum -= root.val;

        //如果sum为0且左右子树都为空,说明存在一条路径
        if(sum == 0 && root.left == null && root.right == null){
            return true;
        }

        //递归左右子树,存在一条即可
        return hasPathSum(root.left,sum) || hasPathSum(root.right,sum);
    }
}
  1. 路径总和(路径集合):找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
class Solution {

    ArrayList<List<Integer>> result = new ArrayList<>();
    ArrayList<Integer> list = new ArrayList<>();

    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        /*
        路经总和,找路径组合
        8.23
        */

        dfs(root,sum);

        return result;
    }

    public void dfs(TreeNode node,int sum){
        //递归出口
        if(node == null){
            return;
        }

        //list先添加当前节点值
        list.add(node.val);
        //sum递减当前节点值
        sum -= node.val;

        //如果sum递减到0并且左右子树为空,result添加list。否则继续递归左右子树
        if(sum == 0 && node.left == null && node.right == null){
            result.add(new ArrayList<>(list));
        }else{
            dfs(node.left,sum);
            dfs(node.right,sum);
        }

        //回溯
        list.remove(list.size()-1);
    }
}
  1. 二叉树的左/右视图:给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
class Solution {

    ArrayList<Integer> result = new ArrayList<>();
    int depth = 0;

    public List<Integer> rightSideView(TreeNode root) {
        /*
        二叉树的右视图,先递归右子树,搜完右子树就return。需要一个深度depth变量控制添加每一层的值
        8.23
        */

        if(root == null){
            return result;
        }

        //当前深度为0
        dfs(root,0);

        return result;
    }

    public void dfs(TreeNode root,int curDepth){
        //递归出口,搜完右子树就return
        if(root == null){
            return;
        }

        //如果当前深度一致
        if(curDepth == depth){
            result.add(root.val);
            depth++;
        }

        //先搜索右子树
        dfs(root.right,curDepth+1);
        dfs(root.left,curDepth+1);
    }
}
  1. 二叉树最大路径和:从随机节点出发到随即节点所能组成的最大路径和是多少
输入: [-10,9,20,null,null,15,7]

   -10
   / \
  9  20
    /  \
   15   7

输出: 42
class Solution {

    int result = Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        /*
        二叉树中的最大路径和:从任意节点出发到任意节点的路径所组成的最大路径的和为多少
        8.23
        */

        dfs(root);

        return result;
    }

    public int dfs(TreeNode root){
        //递归出口
        if(root == null){
            return 0;
        }

        //左子树最大值,和0比较
        int left = Math.max(0,dfs(root.left));

        //右子树最大值,和0比较
        int right = Math.max(0,dfs(root.right));

        //提取本次递归结果,和result比较
        result = Math.max(result,left + right + root.val);

        //返回本次结果:root + max(左,右)
        return root.val + Math.max(left,right);
    }
}

5. 二维dfs:典型岛屿3问、单词搜索

  1. 单词搜索:可以直接修改原矩阵或者使用visited[][]绑定
class Solution {
    public boolean exist(char[][] board, String word) {
        /*
        单词搜索:二维dfs,要判断重复搜索,可以直接修改原矩阵为不属于矩阵数据类型的值,比如'/'。也可以使用visited数组判断。
        8.23
        */

        char[] str = word.toCharArray();

        //遍历矩阵
        for(int i=0;i<board.length;i++){
            for(int j=0;j<board[0].length;j++){
                //返回dfs搜索的结果
                return dfs(i,j,board,str,0);
            }
        }

        return true;
    }

    public boolean dfs(int row,int col,char[][] board,char[] str,int index){
        //false出口
        if(row < 0 || row >= board.length || col < 0 || col >= board[0].length || board[row][col] != str[index]){
            return false;
        }

        //true出口,如果index能够到达str.length-1
        if(index == str.length-1){
            return true;
        }

        //修改当前字符为'/',防止重复搜索
        board[row][col] = '/';

        //上下左右搜索
        boolean result = dfs(row+1,col,board,str,index+1) || dfs(row-1,col,board,str,index+1) || dfs(row,col+1,board,str,index+1) || dfs(row,col-1,board,str,index+1);

        return result;
    }
}
  1. 岛屿周长:当数字为1时进行dfs。统计递归结果。因为矩阵只有0和1,可以直接修改原矩阵来防止重复搜索
class Solution {
    public int islandPerimeter(int[][] grid) {
        /*
        岛屿周长:当数字为1时,进行dfs。统计递归结果,如果已经搜索过,返回0,否则返回1
        8.23
        */

        if(grid.length == 0 || grid[0].length == 0){
            return 0;
        }

        for(int i=0;i<grid.length;i++){
            for(int j=0;j<grid[0].length;j++){
                if(grid[i][j] == 1){
                    return dfs(i,j,grid);
                }
            }
        }

        return 0;
    }

    public int dfs(int row,int col,int[][] grid){
        //1出口:当上下左右为0时,边长+1
        if(row < 0 || row >= grid.length || col < 0 || col >= grid[0].length || grid[row][col] == 0){
            return 1;
        }

        //0出口
        if(grid[row][col] == 2){
            return 0;
        }

        //绑定当前位置
        grid[row][col] = 2;

        //返回递归上下左右的结果
        return dfs(row+1,col,grid) + dfs(row-1,col,grid) + dfs(row,col+1,grid) + dfs(row,col-1,grid);
    }
}
  1. 岛屿数量:当字符为1时进行dfs,完成一次就+1。可以直接修改原矩阵
class Solution {
    public int numIslands(char[][] grid) {
        /*
        岛屿数量:当字符为1时进行dfs,完成一次,数量 + 1。
        8.23
        */

        if(grid.length == 0 || grid[0].length == 0){
            return 0;
        }

        int result = 0;
        for(int i=0;i<grid.length;i++){
            for(int j=0;j<grid[0].length;j++){
                if(grid[i][j] == '1'){
                    dfs(i,j,grid);

                    result++;
                }
            }
        }

        return result;
    }

    public void dfs(int row,int col,char[][] grid){
        //递归出口,
        if(row < 0 || row >= grid.length || col < 0 || col >= grid[0].length || grid[row][col] == '0'){
            return;
        }

        //绑定当前位置,修改为0
        grid[row][col] = '0';

        //搜索上下左右
        dfs(row+1,col,grid);
        dfs(row-1,col,grid);
        dfs(row,col+1,grid);
        dfs(row,col-1,grid);

    }
}
  1. 岛屿最大面积:Math.max()比较每次的结果
class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        /*
        岛屿最大面积:max比较每次递归结果
        8.23
        */

        if(grid.length == 0 || grid[0].length == 0){
            return 0;
        }

        int result = 0;
        for(int i=0;i<grid.length;i++){
            for(int j=0;j<grid[0].length;j++){
                if(grid[i][j] == 1){
                    result = Math.max(result,dfs(i,j,grid));
                }
            }
        }

        return result;
    }

    public int dfs(int row,int col,int[][] grid){
        
        //递归0出口:grid[row][col] = 0
        if(row < 0 || row >= grid.length || col < 0 || col >= grid[0].length || grid[row][col] == 0){
            return 0;
        }

        //绑定当前位置
        grid[row][col] = 0;

        //没有就为1
        int size = 1;

        return size + dfs(row+1,col,grid) + dfs(row-1,col,grid) + dfs(row,col+1,grid) + dfs(row,col-1,grid);

    }
}

6. 图经典问题(还没搞懂...):朋友圈、课程表

  1. 朋友圈
class Solution {
    public int findCircleNum(int[][] M) {
        /*
        朋友圈:图的邻接块.DFS 整体思路:假设以第一个学生为例,首先应找到与该学生是朋友的所有人,找到一个朋友时且没有并访问,则找到该朋友的朋友,这样就能找到与第一个学生有间接关系的朋友。 依次思路遍历所有未被访问的学生,同时朋友圈数+1。
        8.23
        */

        //从1个点开始搜索没有被标记的点,要用visited进行标记搜索过的点

        boolean[] visited = new boolean[M.length];

        int result = 0;

        //行
        for(int i=0;i<M.length;i++){
            //搜索未被标记的点
            if(visited[i] == false){
                dfs(i,M,visited);

                result++;
            }
        }

        return result;
    }

    public void dfs(int row,int[][] M,boolean[] visited){

        for(int i=0;i<M[0].length;i++){
            //搜索未被标记的点
            if(visited[i] == false && M[row][i] == 1){

                visited[i] = true;

                dfs(i,M,visited);
            }
        }
    }
}
  1. 课程表1:你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。在选修某些课程之前需要一些先修课程。
输入: 2, [[1,0]] 
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。

输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。

有向无环图:即课程间规定了前置条件,但不能构成任何环路,否则课程前置条件将不成立。

//BFS拓扑排序(重点)
1. 创建入度表,遍历矩阵,存入indegress[i[0]]++
2. 创建队列,遍历入度表,如果入度表为0,queue.add(i)
3. 当队列不为空时,弹出队首,学过的课程可以删除了:courses--。遍历矩阵,如果pre != p[i],continue。否则indegress[p[0]]--,如果indegress[p[0]] == 0,queue.add(i)
4. return courses == 0。查看能否学完

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        /*
        BFS入度表
        1. 创建入度表,遍历矩阵,存入indegress[i[0]]++
        2. 创建队列,遍历入度表,如果入度表为0,queue.add(i)
        3. 当队列不为空时,弹出队首,courses--。遍历矩阵,如果pre != p[i],continue。否则indegress[p[0]]--,如果indegress[p[0]] == 0,queue.add(i)
        */

        Queue<Integer> queue = new LinkedList<>();
        int[] indegress = new int[numCourses];

        //遍历矩阵,初始化indegress[i[0]]
        for(int[] i : prerequisites){
            indegress[i[0]]++;
        }

        //遍历courses,初始化queue,当indegress[i] == 0时,添加该i
        for(int i=0;i<numCourses;i++){
            if(indegress[i] == 0){
                queue.add(i);
            }
        }

        //当queue不为空,弹出队首,用于在图中搜索与入度表中删除
        while(!queue.isEmpty()){
            int pre = queue.poll();
            numCourses--;

            for(int[] p : prerequisites){
                if(p[1] != pre){
                    continue;
                }

                if(--indegress[p[0]] == 0){
                    queue.add(p[0]);
                }
            }
        }

        return numCourses == 0;
    }
}
  1. 课程表2:返回路径
class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        /*
        BFS入度表,与207稍微不同,学过的课程存入一个数组中
        1. 创建入度表,初始化indegress[p[0]]++
        2. 创建队列,初始化队列,当indegress[i]==0时,queue.add(i)
        3. 创建result数组,index索引
        4/ 当队列不为空时,弹出队首pre,将本次课程存入result。遍历矩阵,如果--courses[i]==0,p[0]入队。如果p[1] != pre,continue

        8.24
        */

        //1. 创建入度表
        int[] indegress = new int[numCourses];
        //2. 遍历矩阵,初始化入度表
        for(int[] p : prerequisites){
            indegress[p[0]]++;
        }

        //3. 创建队列
        Queue<Integer> queue = new LinkedList<>();
        //4. 遍历courses,初始化队列
        for(int i=0;i<numCourses;i++){
            if(indegress[i] == 0){
                queue.add(i);
            }
        }

        int[] result = new int[numCourses];
        int index = 0;
        //5. 当队列不为空时
        while(!queue.isEmpty()){
            int pre = queue.poll();
            result[index++] = pre;

            //6. 遍历矩阵
            for(int[] p : prerequisites){
                if(p[1] != pre){
                    continue;
                }

                if(--indegress[p[0]] == 0){
                    queue.add(p[0]);
                }
            }
        }

        //如果能学完,返回result,否则返回空
        if(index == numCourses){
            return result;
        }else{
            return new int[0];
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容