BFS(1091. 二进制矩阵中的最短路径、279. 完全平方数、127. 单词接龙)

目录

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

1.1 题目描述

1.2 代码

1.2.1 广度优先搜索

二、279. 完全平方数

2.1 题目描述

2.2 代码

2.2.1 数学方法——四平方定理

2.2.2 动态规划

三、127. 单词接龙

3.1 题目描述

3.2 代码

3.2.1 广度优先遍历


BFS广度优先搜索

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

1.1 题目描述

给你一个 n x n 的二进制矩阵 grid 中,返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径,返回 -1 。(难度中等)

二进制矩阵中的 畅通路径 是一条从 左上角 单元格(即,(0, 0))到 右下角 单元格(即,(n - 1, n - 1))的路径,该路径同时满足下述要求:

路径途经的所有单元格都的值都是 0 。
路径中所有相邻的单元格应当在 8 个方向之一 上连通(即,相邻两单元之间彼此不同且共享一条边或者一个角)。
畅通路径的长度 是该路径途经的单元格总数。

          

1.2 代码

 

思路:direction[] 代表8个方向的数组,队列queue用来记录可达点。

我们还需要一个数组标记此位置是否走过,因为题目要求经过的位置要为0,因为若一个点走过我们可将其在grids数组中位置标记为1,代表不可走,这样就节省了存储空间。

分析可达点时有8个方向,先判断可达点的八个是否越界,是否走过,若满足条件入队。

补充:

Queue 中 add() 和 offer()都是用来向队列添加一个元素。
在容量已满的情况下,add() 方法会抛出IllegalStateException异常,offer() 方法只会返回 false 。 

    Pair<Integer, String> pair = new Pair<>(1, "One");
    Integer key = pair.getKey();
    String value = pair.getValue();

1.2.1 广度优先搜索

public static int shortestPathBinaryMatrix(int[][] grids) {
        if (grids == null || grids.length == 0 || grids[0].length == 0) {
            return -1;
        }
        int[][] direction = {{1, -1}, {1, 0}, {1, 1}, {0, -1}, {0, 1}, {-1, -1}, {-1, 0}, {-1, 1}};
        int m = grids.length, n = grids[0].length;//m为行数,n为列数
        Queue<Pair<Integer, Integer>> queue = new LinkedList<>();
        queue.add(new Pair<>(0, 0));//起点入队
        int pathLength = 0;
        while (!queue.isEmpty()) {
            int size = queue.size();
            pathLength++;
            while (size-- > 0) {//队列长度减1
                Pair<Integer, Integer> cur = queue.poll();
                int cr = cur.getKey(), cc = cur.getValue();//cr为行坐标,cc为列坐标
                if (grids[cr][cc] == 1) {
                    continue;//跳过此次循环
                }
                if (cr == m - 1 && cc == n - 1) {//到达终点了
                    return pathLength;
                }
                grids[cr][cc] = 1; // 标记
                for (int[] d : direction) {
                    int nr = cr + d[0], nc = cc + d[1];
                    if (nr >= 0 && nr < m && nc >= 0 && nc < n && grids[nr][nc] == 0) {
                        queue.add(new Pair<>(nr, nc));
                    }
                }
            }
        }
        return -1;
    }

 

二、279. 完全平方数

2.1 题目描述

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

给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

示例 1:输入:n = 12
输出:3 
解释:12 = 4 + 4 + 4

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

 

2.2 代码

思路:https://leetcode-cn.com/problems/perfect-squares/solution/wan-quan-ping-fang-shu-by-leetcode/

五种方法,实在是写的太好了!!

2.2.1 数学方法——四平方定理

博文中的方法五:四平方定理

  public static int numSquares(int n) {
        if (isSquares(n)){
            return 1;
        }
        //化简n
        while (n%4==0) {
            n = n / 4;
        }
        if (n%8==7)
          return 4;
        for (int i = 0; i*i < n; i++) {
            if (isSquares(n-i*i))
                return 2;
        }

        return 3;
    }
    public static boolean isSquares(int n){
        int sq= (int) Math.sqrt(n);
//        if (sq*sq==n)
//            return true;
//        else return false;
        return sq*sq==n;
    }

 

补充:

Java Set.contains()方法:判断Set集合是否包含指定的对象。

contains()方法是通过HashSet容器内对象的两个方法进行判断的。

第一个是hashCode()方法。通过此方法可以快速确定希望添加的元素是否不存在于该容器。如果不存在,那hash值一定不相同。当hash值比较有相同时,继续通过equals方法进行比较。

Object类对equals方法的定义是比较两个对象的内存地址是否一样,如果希望比较值或者其他数据判断两个对象是否相等,那就要重写equals方法。同时,因为contains()是先调用hashCode()方法, 因此也需要重写hashCode()方法。
 

2.2.2 动态规划

解决暴力方法,递归中堆栈溢出的问题的一个思路就是使用动态规划(DP)技术,该技术建立在重用中间解的结果来计算终解的思想之上。

多维数组 DP 来保存中间子解的值。

n=12时,

1. 预计算小于给定数字 n 的完全平方数列表(即 square_nums)

2. dp[ ]

最终返回dp[n],即为最终答案。

class Solution {

 public static int numSquares(int n) {
      int[] dp=new int[n+1];
        Arrays.fill(dp,Integer.MAX_VALUE);
        dp[0]=0;
         
    //预计算小于给定数字 n 的完全平方数列表(即 square_nums)
        int square_nums_len =(int)Math.sqrt(n)+1;
        int[]  square_nums= new int[square_nums_len];
        for (int i = 1; i <square_nums_len ; i++) {
            square_nums[i]=i*i;
        }

        //填写dp[],dp[i]放的是i可由几个平方数组合的最小值个数
        for (int i = 1; i <= n; i++) {
            for (int s = 1; s < square_nums_len; s++){
                if (i<square_nums[s]){
                    break;
                }
                dp[i]=Math.min(dp[i],dp[i-square_nums[s]]+1);
            }
        }
        //System.out.println(dp[n]);
        return dp[n];
  }
}

三、127. 单词接龙

3.1 题目描述

字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:(难度困难)

  • 序列中第一个单词是 beginWord 。
  • 序列中最后一个单词是 endWord 。
  • 每次转换只能改变一个字母。
  • 转换过程中的中间单词必须是字典 wordList 中的单词。

给你两个单词 beginWord 和 endWord 和一个字典 wordList ,找到从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回0。

 
示例 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" 不在字典中,所以无法进行转换。

一般会考虑消耗的时间是多少,时间比空间重要。

3.2 代码

3.2.1 广度优先遍历

思路:https://leetcode-cn.com/problems/word-ladder/solution/yan-du-you-xian-bian-li-shuang-xiang-yan-du-you-2/

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;

public class Solution {

    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        // 第 1 步:先将 wordList 放到哈希表里,便于判断某个单词是否在 wordList 里
        Set<String> wordSet = new HashSet<>(wordList);
        if (wordSet.size() == 0 || !wordSet.contains(endWord)) {
            return 0;
        }
        wordSet.remove(beginWord);
        
        // 第 2 步:图的广度优先遍历,必须使用队列和表示是否访问过的 visited 哈希表
        Queue<String> queue = new LinkedList<>();
        queue.offer(beginWord);
        Set<String> visited = new HashSet<>();
        visited.add(beginWord);
        
        // 第 3 步:开始广度优先遍历,包含起点,因此初始化的时候步数为 1
        int step = 1;
        while (!queue.isEmpty()) {
            int currentSize = queue.size();
            for (int i = 0; i < currentSize; i++) {
                // 依次遍历当前队列中的单词
                String currentWord = queue.poll();
                // 如果 currentWord 能够修改 1 个字符与 endWord 相同,则返回 step + 1
                if (changeWordEveryOneLetter(currentWord, endWord, queue, visited, wordSet)) {
                    return step + 1;
                }
            }
            step++;
        }
        return 0;
    }

    /**
     * 尝试对 currentWord 修改每一个字符,看看是不是能与 endWord 匹配
     *
     * @param currentWord
     * @param endWord
     * @param queue
     * @param visited
     * @param wordSet
     * @return
     */
    private boolean changeWordEveryOneLetter(String currentWord, String endWord,
                                             Queue<String> queue, Set<String> visited, Set<String> wordSet) {
        char[] charArray = currentWord.toCharArray();
        for (int i = 0; i < endWord.length(); i++) {
            // 先保存,然后恢复
            char originChar = charArray[i];
            for (char k = 'a'; k <= 'z'; k++) {
                if (k == originChar) {
                    continue;
                }
                charArray[i] = k;
                String nextWord = String.valueOf(charArray);
                if (wordSet.contains(nextWord)) {
                    if (nextWord.equals(endWord)) {
                        return true;
                    }
                    if (!visited.contains(nextWord)) {
                        queue.add(nextWord);
                        // 注意:添加到队列以后,必须马上标记为已经访问
                        visited.add(nextWord);
                    }
                }
            }
            // 恢复
            charArray[i] = originChar;
        }
        return false;
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值