目录
一、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广度优先搜索
给你一个 n x n 的二进制矩阵 grid 中,返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径,返回 -1 。(难度中等)
二进制矩阵中的 畅通路径 是一条从 左上角 单元格(即,(0, 0))到 右下角 单元格(即,(n - 1, n - 1))的路径,该路径同时满足下述要求:
路径途经的所有单元格都的值都是 0 。
路径中所有相邻的单元格应当在 8 个方向之一 上连通(即,相邻两单元之间彼此不同且共享一条边或者一个角)。
畅通路径的长度 是该路径途经的单元格总数。
思路:direction[] 代表8个方向的数组,队列queue用来记录可达点。
我们还需要一个数组标记此位置是否走过,因为题目要求经过的位置要为0,因为若一个点走过我们可将其在grids数组中位置标记为1,代表不可走,这样就节省了存储空间。
分析可达点时有8个方向,先判断可达点的八个是否越界,是否走过,若满足条件入队。
补充:
Queue 中 add() 和 offer()都是用来向队列添加一个元素。
在容量已满的情况下,add() 方法会抛出IllegalStateException异常,offer() 方法只会返回 false 。
Pair pair = new Pair<>(1, "One");
Integer key = pair.getKey();
String value = pair.getValue();
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> 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 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;
}
给定正整数 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
思路:https://leetcode-cn.com/problems/perfect-squares/solution/wan-quan-ping-fang-shu-by-leetcode/
五种方法,实在是写的太好了!!
博文中的方法五:四平方定理
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()方法。
解决暴力方法,递归中堆栈溢出的问题的一个思路就是使用动态规划(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
字典 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" 不在字典中,所以无法进行转换。
一般会考虑消耗的时间是多少,时间比空间重要。
思路: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 wordList) {
// 第 1 步:先将 wordList 放到哈希表里,便于判断某个单词是否在 wordList 里
Set wordSet = new HashSet<>(wordList);
if (wordSet.size() == 0 || !wordSet.contains(endWord)) {
return 0;
}
wordSet.remove(beginWord);
// 第 2 步:图的广度优先遍历,必须使用队列和表示是否访问过的 visited 哈希表
Queue queue = new LinkedList<>();
queue.offer(beginWord);
Set 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 queue, Set visited, Set 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;
}
}