leetcode系列之 BFS

写在前面:该系列按照标签进行刷题,参考 leetcode标签

广度优先搜索 BFS

1091.二进制矩阵中的最短路径

在一个 N × N 的方形网格中,每个单元格有两种状态:空(0)或者阻塞(1)。

一条从左上角到右下角、长度为 k 的畅通路径,由满足下述条件的单元格 C_1, C_2, ..., C_k 组成:

  • 相邻单元格 C_iC_{i+1} 在八个方向之一上连通(此时,C_iC_{i+1} 不同且共享边或角)

  • C_1 位于 (0, 0)(即,值为 grid[0][0]

  • C_k 位于 (N-1, N-1)(即,值为 grid[N-1][N-1]

  • 如果 C_i 位于 (r, c),则 grid[r][c] 为空(即,grid[r][c] == 0

返回这条从左上角到右下角的最短畅通路径的长度。如果不存在这样的路径,返回 -1 。

看到最短路径,可以考虑 BFS,利用 grid 表达当前点的路径。


BFS:

class Solution {
    private int[][] dist={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
    public int shortestPathBinaryMatrix(int[][] grid) {
        // 10:03
        // 从左上角到右下角,多种路径选择其中一条
        int rows=grid.length;
        int cols=grid[0].length;
        if(grid[0][0]==1 || grid[rows-1][cols-1]==1) return -1;
        Queue<int []> queue=new LinkedList<>();
        int fir[]={0,0};//起点
        grid[0][0]=1;//利用 grid 记录距离
        queue.offer(fir);
        // grid[rows-1][cols-1]==0 代表还没有走到终点
        while(!queue.isEmpty() && grid[rows-1][cols-1]==0){
            int temp[]=queue.poll();
            int row=temp[0];
            int col=temp[1];
            for(int i=0;i<8;i++){
                int newx=row+dist[i][0];
                int newy=col+dist[i][1];
                if(newx <0 || newx>=rows || newy <0|| newy >=cols) continue;
                if(grid[newx][newy]==0){//判断新点是否已经到达
                    grid[newx][newy]=grid[row][col]+1;
                    queue.offer(new int[]{newx,newy});
                }
            }
        }
        // 若最后末尾点为0,证明没有路径可以到达,否则返回末尾点的值即路径
        return grid[rows-1][cols-1]==0 ? -1:grid[rows-1][cols-1];
    }
}

279.完全平方数

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

//示例 1:

输入: n = 12
输出: 3 
解释: 12 = 4 + 4 + 4.
//示例 2:

输入: n = 13
输出: 2
解释: 13 = 4 + 9.

BFS:相当于构建一个树。第一层是n,第二层是n减去每个完全平方数的结果,第三层是第二层减之后的结果,哪一层先出现0,层数即个数。

动态规划:状态转移方程:dp[i]=min(dp[i],dp[i-j*j]+1),类似硬币问题。


1.BFS:

class Solution {
    public int numSquares(int n) {
        // 10:44
        // 1 4 9 16 25
        List<Integer> squares=square(n);
        Queue<Integer> queue=new LinkedList<>();
        boolean flag[]=new boolean[n+1];
        queue.offer(n);
        flag[n]=true;
        int num=0;
        // 对n减完全平方数,当结果为0时即所需个数
        while(!queue.isEmpty()){
            int size=queue.size();
            num++;
            while(size-- >0){
                int cur=queue.poll();
                for(int s:squares){
                    int res=cur-s;
                    if(res==0) return num;
                    if(res<0) break;
                    if(flag[res]) continue;//若结果值已访问则进行下一次遍历
                    flag[res]=true;
                    queue.offer(res);
                }
            }
        }
        return num;
    }
    //生成小于n的完全平方数列表
    public List<Integer> square(int n){
        List<Integer> res=new ArrayList<>();
        int square=1;
        int count=1;
        while(square<=n){
            res.add(square);
            count++;
            square=count*count;
        }
        return res;
    }
}

2.动态规划

class Solution {
    public int numSquares(int n) {
        // 10:44
        // DP :状态转移方程:dp[i]=min(dp[i],dp[i-j*j]+1);
        if(n<1) return 0;
        int[] dp=new int[n+1];
        for(int i=1;i<n+1;i++){
            dp[i]=i;//最坏情况,全部由1组成
            for(int j=1;i-j*j>=0;j++){
                dp[i]=Math.min(dp[i],dp[i-j*j]+1);
            }
        }
        return dp[n];
    }

}

127.单词接龙

给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:

  • 每次转换只能改变一个字母。

  • 转换过程中的中间单词必须是字典中的单词。

说明:

  • 如果不存在这样的转换序列,返回 0。
  • 所有单词具有相同的长度。
  • 所有单词只由小写字母组成。
  • 字典中不存在重复的单词。
  • 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
//示例 1:
输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

输出: 5

解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
     返回它的长度 5。

//示例 2:
输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

输出: 0

解释: endWord "cog" 不在字典中,所以无法进行转换。

BFS:重点是找到只差一个单词的字符串,然后每次遍历一个字符串,找寻和它相差一个的字符串,判断是否是目标字符串。


1.BFS:

class Solution {
  public int ladderLength(String beginWord, String endWord, List<String> wordList) {
    // 11:35

    int L = beginWord.length();
    // 存储通用字符和字典中与其对应的字符串列表:d*g->{dog,dig}
    Map<String, List<String>> allComboDict = new HashMap<>();

    wordList.forEach(
        word -> {
          for (int i = 0; i < L; i++) {
            String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L);
            // 找到字典中通用字符对应的字符列表,若为空则创建一个新列表
            List<String> transformations = allComboDict.getOrDefault(newWord, new ArrayList<>());
            transformations.add(word);
            allComboDict.put(newWord, transformations);
          }
        });

    // pair 存储字符串与转换到该字符串所需的次数
    Queue<Pair<String, Integer>> Q = new LinkedList<>();
    Q.add(new Pair(beginWord, 1));

    // 访问标志
    Map<String, Boolean> visited = new HashMap<>();
    visited.put(beginWord, true);
    // BFS
    while (!Q.isEmpty()) {
      Pair<String, Integer> node = Q.remove();
      String word = node.getKey();
      int level = node.getValue();
      for (int i = 0; i < L; i++) {

        //当前字符串的通用字符形式
        String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L);

        // 从map中获得字典中该通用字符对应的字符串列表
        for (String adjacentWord : allComboDict.getOrDefault(newWord, new ArrayList<>())) {
          //如果找到字符串对应目标字符串,则返回层数+1
          if (adjacentWord.equals(endWord)) {
            return level + 1;
          }
          // 如果不等则判断是否已访问,未访问则置为访问,同时加入队列
          if (!visited.containsKey(adjacentWord)) {
            visited.put(adjacentWord, true);
            Q.add(new Pair(adjacentWord, level + 1));
          }
        }
      }
    }

    return 0;
  }
}
posted @ 2020-05-15 10:25  雨落成尘  阅读(387)  评论(0编辑  收藏  举报