DFS和BFS在算法题中的通用模版,遇到类似题直接套用

DFS和BFS在算法题中的通用模版,遇到类似题直接套用!

一. 前言

在常见的代码中,广度优先遍历(DFS)以及深度优先遍历(BFS)是非常常见的两种回溯和递归应用场景,用来遍历所有的情况,将符合算法题目要求的情况添加进答案集合中。实际上所有的DFS和BFS代码都非常类似,所以本文抽象出广度优先遍历和深度优先遍历的通用模版。

二. DFS

针对广度优先遍历的场景,通常思路是传入某一个起点,然后dfs函数从起点开始,对起点的上下左右依次遍历(以上下左右为起点递归调用dfs函数),以此来对所有情况进行遍历。

2.1 首先通过例题来体会

牛客BM57题:岛屿数量:
给一个01矩阵,1代表是陆地,0代表海洋, 如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻。
岛屿: 相邻陆地可以组成一个岛屿(相邻:上下左右) 判断岛屿个数。
例如:输入
[[1,1,0,0,0],
[0,1,0,1,1],
[0,0,0,1,1],
[0,0,0,0,0],
[0,0,1,1,1]]
对应的输出为3(注:存储的01数据其实是字符’0’,‘1’)

本题是一道典型的dfs场景题,当我们遇到某个‘1’的时候,需要把它周围连通的岛都给标记,以防止重复计算,这个标记周围连通的过程就需要用dfs。这里我们直接给出答案:

   int n ;
   int m ;
   boolean[][] isUsed;
   char[][] grid;
   public int numIslands(char[][] grid) {
       n = grid.length;
       m = grid[0].length;
       isUsed = new boolean[n][m];
       this.grid = grid;
       int sum = 0;

       for (int i = 0; i < n; i++) {
           for (int j = 0; j < m; j++) {
               if (grid[i][j] == '1' && !isUsed[i][j]){
                   dfs(i,j);
                   sum++;
               }
           }
       }

       return sum;
   }


   int[][] direction = {{-1,0},{0,1},{1,0},{0,-1}};
   private void dfs(int x,int y){
       if(x < 0 || y < 0 || x >= n || y>= m  || grid[x][y] == '0' || isUsed[x][y]) {
           return;
       }

       isUsed[x][y] = true;

       for(int t = 0; t < 4; t++) {
           int nextX = x + direction[t][0];
           int nextY = y + direction[t][1];
           dfs(nextX, nextY);
       }
   }

2.2 抽象出DFS的模版

	int m ;
    int n ;
    boolean[][] isUsed;
    char[][] map;
    int ans ;
    public int modelOfDfs(char[][] map) {
        m = grid.length;
        n = grid[0].length;
        isUsed = new boolean[n][m];
        this.map = map;
        ans = 0;

       if(//找到某个起点,从起点开始遍历){
       	  dfs(i,j);
       }
        return ans;
    }


    int[][] direction = {{-1,0},{0,1},{1,0},{0,-1}};
    private void dfs(int x,int y){
        if(//边界条件) {
            return;
        }

        /*
        *这里做命中之后需要做的操作
        */

		//上下左右递归遍历
        for(int t = 0; t < 4; t++) {
            int nextX = x + direction[t][0];
            int nextY = y + direction[t][1];
            dfs(nextX, nextY);
        }
    }

三. BFS

在深度优先遍历的场景,通常思路是对当前值考虑用或者不用,用的话可以看作是挂在二叉树的子节点上,这样所有的从根节点到叶子节点的路径即是所有的答案合集。

3.1 同样通过例题来体会

牛客BM55题:没有重复项数字的全排列:
给出一组数字,返回该组数字的所有排列
例如:
[1,2,3]的所有排列如下
[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2], [3,2,1].
(以数字在数组中的位置靠前为优先级,按字典序排列输出。)

对于每个数字,我们当前可以选择取或者不取,不取的放到后面的位置去,答案如下:

   int[] num;
   boolean[] isUsed;
   int n;
   ArrayList<ArrayList<Integer>> ans;
   public ArrayList<ArrayList<Integer>> permute(int[] num) {
       n = num.length;
       this.num = num;
       ans = new ArrayList<>();
       ArrayList<Integer> path = new ArrayList<>();
       isUsed = new boolean[n];
       dfs(path);
       return ans;
   }

   public void dfs(ArrayList<Integer> path){
       if (path.size()==n){
           ans.add(new ArrayList<>(path));
           return;
       }

       //从剩下的没用过的选一个添加进去;
       for (int i = 0; i < n; i++) {
           if (!isUsed[i]){
               path.add(num[i]);
               isUsed[i] = true;
               dfs(path);
               isUsed[i] = false;
               path.remove(path.size()-1);
           }
       }
   }

3.2 抽象出BFS的模版

	int[] num;
    boolean[] isUsed;
    int n;
    ArrayList<ArrayList<Integer>> ans;
    public ArrayList<ArrayList<Integer>> modelOfBfs(int[] num) {
        n = num.length;
        this.num = num;
        ans = new ArrayList<>();
        ArrayList<Integer> path = new ArrayList<>();
        isUsed = new boolean[n];
        dfs(path);
        return ans;
    }

    public void dfs(ArrayList<Integer> path){
        if (//边界条件){
            ans.add(new ArrayList<>(path));
            return;
        }

        //从剩下的没用过的选一个添加进去;
        for (int i = 0; i < n; i++) {
            if (!isUsed[i]){
                path.add(num[i]);
                isUsed[i] = true;
                dfs(path);
                isUsed[i] = false;
                path.remove(path.size()-1);
            }
        }
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值