【寸铁的刷题笔记】树、回溯、图论、bfs、dfs(四)
大家好 我是寸铁👊
金三银四,图论基础
、回溯
结合bfs
、dfs
是必考的知识点✨
快跟着寸铁刷起来!面试顺利上岸👋
喜欢的小伙伴可以点点关注 💝
🌞详见如下专栏🌞
🍀🍀🍀寸铁的刷题笔记🍀🍀🍀
17. 电话号码的字母组合
考点
dfs、回溯
代码
class Solution {
//创建一个列表,用于存储组合的结果
List<String>list = new ArrayList<>();
public List<String> letterCombinations(String digits) {
//如果说digits字符串为空或者长度为0
//则说明没有任何可以组合的字符,直接返回空列表即可。
if(digits == null || digits.length() == 0)return list;
//使用数组存储一个映射,也可以使用map<Integer , String>存储
String[]numString = {
" ",//下标为0
" ",//下标为1
"abc",//2
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
};
//递归
//传入参数: 数字字符串,numString映射数组,当前搜索的层数
backTracing(digits , numString , 0);
//返回最后的列表
return list;
}
//用stringbuilder进行拼接
StringBuilder temp = new StringBuilder();
public void backTracing(String digits , String[] numString , int num){
//如果说最后的层数等于数字的长度
//则说明已经搜索完了,入队即可。
if(num == digits.length()){
list.add(temp.toString());
return;
}
//获取数字所对应的映射字符串
String str = numString[digits.charAt(num) - '0'];
//进行dfs暴搜
//枚举第一个数字
for(int i = 0; i < str.length(); i++){
//添加到temp中
temp.append(str.charAt(i));
//继续向下一层递归
backTracing(digits , numString , num + 1);
//恢复现场,向上回溯
temp.deleteCharAt(temp.length() - 1);//向上回溯
}
}
}
77. 组合
考点
dfs、回溯
代码
class Solution {
List<List<Integer>>result = new ArrayList<>();
LinkedList<Integer>path = new LinkedList<>();
//可以删除最后一个元素 removeLast()
//再转为ArrayList<>()
public List<List<Integer>> combine(int n, int k) {
combineHelper(n , k ,1);//确定入参 n表示数字1 - n k 表示组合的大小
return result;//返回最终的结果
}
public void combineHelper(int n , int k , int startIndex){
if(path.size() == k){
result.add(new ArrayList<>(path));
return;
}
for(int i = startIndex; i <= n; i++){
//添加元素
path.add(i);
//递归到下一层 注意: 起始数字从 i + 1开始,确保数字组合不重复。
//每次递归时,都从上一个数字的下一个数开始,这样就不会导致组合出现重复。
combineHelper(n , k , i + 1);
//向上回溯
path.removeLast();
}
}
}
127. 单词接龙
考点
bfs、宽度搜索、哈希表
思路
先用一个
Map
存储单词列表,后面用于匹配单词使用。
暴力搜索单词长度下的字母每一位的26
种可能,再去与单词列表进行匹配,匹配成功则标记该单词已经走过
。
同时,单词序列的长度(bfs搜索的层数)++
,
如何确保转换序列最短?
这里我们是一层层搜索,每一层优先去匹配最后的单词
。
谁先
匹配的上,谁所在的那一层就是最短的序列长度
。
如果说匹配的上,则直接
返回序列长度
。
如果说匹配不上,则把当前搜索到的节点入队
,用于下一层
的继续搜索。
代码
class Solution {
/*
bfs暴搜
用一个Map存储单词列表,后面用于匹配单词使用。
暴力搜索单词长度下的字母每一位的26种可能,再去与单词列表进行匹配,匹配成功则标记该单词已经走过。
同时,单词序列的长度(bfs搜索的层数)++,单词入队,用于下一轮的搜索。
最后,判断能否走到最后一个单词,也就是当搜索时匹配到最后一个单词时,搜索停止,返回走过的序列长度。
如何确保转换序列最短?
这里我们是一层层搜索,每一层优先去匹配最后单词。
如果说匹配的上,则返回序列长度。
如果说匹配不上,则把当前搜索的节点入队,用于下一层的继续搜索。
*/
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
//如果说开头的单词的长度与最后一个单词的长度不同,则不存在这样的转换序列。
if(beginWord.length() != endWord.length())return 0;
int n = beginWord.length();
//创建一个单词库,用于后面的单词匹配。
Set<String>wordsLib = new HashSet<>();
for(String word : wordList){
wordsLib.add(word);
}
//如果说终点单词不在单词库中则不存在这样的转换序列。
if(!wordsLib.contains(endWord))return 0;
//宽搜
//创建标记Set记录已经走过的节点
Set<String>visited = new HashSet<>();
visited.add(beginWord);//标记起点节点已经被搜索过
//维护一个队列,用于宽搜。
Queue<Node> queue = new LinkedList<>();
queue.add(new Node(beginWord , 1));
while(!queue.isEmpty()){
//弹出当前队列的节点进行宽搜
Node cur = queue.poll();
String curWord = cur.word;//记录弹出节点的单词
int cnt = cur.cnt; //记录弹出节点的单词长度
//每次弹出一个单词,序列的长度就++
//因为要往下一层搜索,是一层层搜索,如果最后找到,则返回找到的序列长度即可
//找不到则返回0
cnt++; //bfs搜索的层数(序列长度)加1
System.out.println(curWord);
System.out.println(cnt);//a --> c
//暴力搜索
//注意:这个搜索的话,是一层层搜。
//每次搜索时,会优先考虑是否与最后一个单词匹配,确保序列长度是最短的。
//如果匹配上,则直接返回序列长度即可,此时的序列长度为最短
//如果没有匹配,则先把这一层搜索到的节点入队,用于下一层搜索。
for(int i = 0 ; i < n; i++){
for(int j = 0; j < 26; j++){
StringBuilder nextWordBuilder = new StringBuilder(curWord);
nextWordBuilder.setCharAt(i , (char)('a' + j));
String nextWord = nextWordBuilder.toString();
//如果说该节点没被遍历过并且单词库中有该节点
if(!visited.contains(nextWord) && wordsLib.contains(nextWord)){
if(nextWord.equals(endWord))return cnt;//如果说暴搜的单词等于最后一个单词则返回序列的长度
System.out.println(nextWord);
visited.add(nextWord);//标记该节点已经被遍历过
queue.offer(new Node(nextWord , cnt));//把该节点入队
}
}
}
}
return 0;//找不到转换序列 则返回0
}
}
//节点类
class Node{
String word;//单词
int cnt;//序列长度
public Node(String word , int cnt){
this.word = word;
this.cnt = cnt;
}
}
124. 二叉树中的最大路径和
考点
dfs、递归
思路
要求
:返回其最大路径和
做法
:路径和:枚举每个点作为起点,加上左右子树的值,取最大值即可。
先递归处理根节点的左右分支,向下走到底,把叶子节点的最大路径和取一个最大值。
回溯
:向上回溯时,会计算上层根节点的路径和,继续取一个最大值。
返回
:由于每个节点只能选一条路走,最大路径和下应该选择左右分支的最大值分支
代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int maxSum = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
//递归函数入口
maxGain(root);
//返回最大路径和
return maxSum;
}
//题意分析:
/*
要求:返回其最大路径和
做法:路径和:枚举每个点作为起点,加上左右子树的值,取最大值即可。
先递归处理根节点的左右分支,向下走到底,把叶子节点的最大路径和取一个最大值。
回溯:向上回溯时,会计算上层根节点的路径和,继续取一个最大值。
返回:由于每个节点只能选一条路走,最大路径和下应该选择左右分支的最大值分支
*/
public int maxGain(TreeNode node){
if(node == null){
return 0;
}
//向左递归,计算左子树的叶子节点的最大路径和
//如果小于0 则不选该分支 因为选了后路径和反而更小
int leftGain = Math.max(maxGain(node.left) , 0);
//向右递归,计算右子树的叶子节点的最大路径和
//如果小于0 则不选该分支 因为选了后路径和反而更小
int rightGain = Math.max(maxGain(node.right) , 0);
//计算
//回溯时,会依次把上层非叶子节点做为根节点,计算其路径和
int priceNewPath = node.val + leftGain + rightGain;
//把每次计算的结果取一个max
maxSum = Math.max(maxSum , priceNewPath);
//返回一条较大路径 给上层节点继续计算路径和
return node.val + Math.max(leftGain , rightGain);
}
}
98. 验证二叉搜索树
考点
DFS、双指针、回溯
与 530. 二叉搜索树的最小绝对差的处理逻辑类似
思路
思想:根据定义,节点的左子树
小于
当前节点,节点的右子树大于
当前节点
也就是left < middle < right
很自然的就想到了用中序遍历
,再判断一下是否满足left < middle < right
即可
进一步,我们只需要判断当前的节点是否小于
之前的节点。
如果小于则不满足条件,向上返回false
即可。
否则,则满足条件。
代码
class Solution {
/*
思想:根据定义,节点的左子树小于当前节点,节点的右子树大于当前节点
也就是left < middle < right
很自然的就想到了用中序遍历,再判断一下是否满足left < middle < right即可
进一步,我们只需要判断当前的节点是否小于之前的节点。
如果小于则不满足条件,向上返回false即可。
否则,则满足条件。
*/
TreeNode pre; //存储上一个节点
public boolean isValidBST(TreeNode root) {
//如果节点为空则直接返回true
if(root == null)return true;
//左 创建一个变量记录左子树递归的结果
boolean left = isValidBST(root.left);
if(!left){
return false;
}
//中 处理条件的逻辑 判断当前节点是否 <= 上一个节点
if(pre != null && root.val <= pre.val){
return false;
}
//一开始为空,记录上一个节点。
pre = root;
//右 创建一个变量记录右子树递归的结果
boolean right = isValidBST(root.right);
//向上返回右子树递归的结果 right
// if(!right){
// return false;
// }
//与下等效,考虑到方法的返回语句,如下编译通过。
return right;
}
}
往期好文💕
保姆级教程
【保姆级教程】Windows11下go-zero的etcd安装与初步使用
【保姆级教程】Windows11安装go-zero代码生成工具goctl、protoc、go-zero
【Go-Zero】手把手带你在goland中创建api文件并设置高亮
报错解决
【Go-Zero】Error: user.api 27:9 syntax error: expected ‘:‘ | ‘IDENT‘ | ‘INT‘, got ‘(‘ 报错解决方案及api路由注意事项
【Go-Zero】Error: only one service expected goctl一键转换生成rpc服务错误解决方案
【Go-Zero】【error】 failed to initialize database, got error Error 1045 (28000):报错解决方案
【Go-Zero】Error 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)报错解决方案
【Go-Zero】type mismatch for field “Auth.AccessSecret“, expect “string“, actual “number“报错解决方案
【Go-Zero】Error: user.api 30:2 syntax error: expected ‘)‘ | ‘KEY‘, got ‘IDENT‘报错解决方案
【Go-Zero】Windows启动rpc服务报错panic:context deadline exceeded解决方案