文章目录
1. 剑指 Offer 12. 矩阵中的路径
原题链接
深度优先搜索
/*
1. dfs,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。
2. dfs怎么写?
1) 参数(board, word, i, j, k)i,j是board中的索引 k是word中的索引
2) 返回值(结束条件):
false: 索引越界/字符不匹配
true: k==word.length-1, 说明完成了全部匹配
3) 满足条件,递归向四个方向搜索
3. 递归
1) 标记已访问元素防止递归时重复搜索。board[i][j] = '\0'
2) 朝当前元素的 上、下、左、右 四个方向开启下层递归,使用 或 连接
【代表只需找到一条可行路径就直接返回,不再做后续 DFS 】
记录结果。
3) 还原当前矩阵元素。board[i][j] = work[k]
*/
class Solution {
public boolean exist(char[][] board, String word) {
char[] s = word.toCharArray();
for(int i=0; i<board.length; i++){
for(int j=0; j<board[0].length; j++){
if(dfs(board, s, i, j, 0)) return true;
}
}
return false;
}
boolean dfs(char[][] board, char[] word, int i, int j, int k){
if(i<0 || i>board.length-1 || j<0 || j>board[0].length-1 || board[i][j]!=word[k]) return false;
if(k == word.length-1) return true;
board[i][j] = '\0';
boolean res = dfs(board, word, i+1, j, k+1) || dfs(board, word, i-1, j, k+1) || dfs(board, word, i, j+1, k+1) || dfs(board, word, i, j-1, k+1);
board[i][j] = word[k];
return res;
}
}
2. 剑指 Offer 13. 机器人的运动范围
原题链接
深度优先搜索
/*
1. dfs, 题中说明从(0, 0)开始移动, 在本题中不需要上下左右四个方向搜索。
下、右两个方向就可以到达所有的坐标点。
2. dfs函数
1) 参数(i,j,si,sj) i,j表示此时坐标,si,sj表示数位和
2) 返回值(结束条件)
0: 越界/已到达过/数位和大于k
1+dfs(): 可以到达此格子,到达数量+1
3. 初始化一个二维数组用来标记已到达过的格子
封装计算数位和的函数,便于计算
*/
class Solution {
int m;
int n;
int k;
boolean[][] visited;
public int movingCount(int m, int n, int k) {
this.m = m; this.n = n; this.k = k;
this.visited = new boolean[m][n];
return dfs(0,0,0,0);
}
int dfs(int i, int j, int si, int sj){
if(i>=m || j>=n || visited[i][j] || si+sj>k) return 0;
visited[i][j] = true; // 已访问
return 1+dfs(i+1,j,sum(i+1),sum(j))+dfs(i,j+1,sum(i),sum(j+1));
}
// 计算数位和
int sum(int n){
int s = 0;
while(n != 0){
s += n%10;
n = n/10;
}
return s;
}
}
3. 剑指 Offer 26. 树的子结构
/*
1. 遍历二叉树A的节点,依次作为根节点与二叉树B比较
2. 一个比较两棵树是否相同的函数recur(A,B)
3. 前序遍历(根节点,左节点,右节点)
recur(A,B)
isSubStructure(A.left, B)
isSubStructure(A.right, B)
*/
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
// 空树不是任意一个树的子结构
if(A == null || B == null) return false;
// 前序遍历
return recur(A,B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);
}
// A中是否有出现和B相同的结构和节点值
boolean recur(TreeNode A, TreeNode B){
if(B == null) return true;
if(A == null || A.val != B.val) return false;
return recur(A.left, B.left) && recur(A.right, B.right);
}
}
4. 剑指 Offer 34. 二叉树中和为某一值的路径
原题链接
先序遍历 + 路径记录
/*
1. 创立两个链表。path存储当前路径,res存储满足条件的路径数组
2. path满足条件则加入res中
3. 满足条件即:到达叶子节点且路径和等于目标值
4. 先序遍历
*/
class Solution {
LinkedList<Integer> path = new LinkedList<>();
LinkedList<List<Integer>> res = new LinkedList<>();
public List<List<Integer>> pathSum(TreeNode root, int target) {
recur(root,target);
return res;
}
void recur(TreeNode root, int tar){
if(root == null) return;
path.add(root.val);
tar -= root.val;
if(tar == 0 && root.left == null && root.right == null)
// 避免直接添加 path 对象,拷贝了一个 path对象加入到res
res.add(new LinkedList(path));
recur(root.left, tar);
recur(root.right, tar);
// 回溯将path中添加值删掉
path.removeLast();
}
}
5. 剑指 Offer 38. 字符串的排列
全排列
/*
1. 全排列
2. 剪枝
3. 使用的数据结构
*/
class Solution {
List<String> res = new LinkedList<>();
char[] c;
public String[] permutation(String s) {
c = s.toCharArray();
dfs(0);
return res.toArray(new String[res.size()]); // 转换为字符串数组
}
void dfs(int n){
if(n == c.length-1){
res.add(String.valueOf(c)); // String.valueOf 将c转换为字符串
return;
}
// 用set来排除字符串数组中重复的字符
HashSet<Character> set = new HashSet<>();
for(int i=n; i<c.length; i++){
if(set.contains(c[i])) continue;
set.add(c[i]);
swap(i,n);
dfs(n+1);
swap(i,n);
}
}
void swap(int a, int b){
char temp = c[a];
c[a] = c[b];
c[b] = temp;
}
}