回溯简单题目练习
图这个东西当然是要先理解一下的参考这个
知乎文章还好大家看不到链接地址,这家伙链接27行,好家伙真吉尔长
DFS:(深度优先搜索算法)
二叉树前、中、后序遍历过程记得不,嗯。。。后面会继续挖
一路一直走、走到头(走不下去没有其他分支)返回到沿途有分支的地方,继续一路走到黑直到走完所有路
BFS:(广度优先算法)
二叉树层序遍历过程,“树”这个萝卜的坑先等等后面会继续挖
还是不强行语言表述了,看图就懂了。
小彩蛋:其实俺写这个的时候先写了彩蛋,因为至少在写这个的时候俺还不知道BFS和DFS是什么,来抱着学习的心态来把这个小东东解释清楚,咱应该就会的89不离10了吧
上手练习:
废话不多说了,原理很简单,貌似以为自己会了?来出个野刀开始杀怪了
《二叉树根节点到叶子节点的所有路径和》:题目地址
package 回溯;
import java.util.ArrayDeque;
import java.util.Queue;
//https://www.nowcoder.com/practice/185a87cd29eb42049132aed873273e83?tpId=196&tqId=37049&ru=/exam/oj
/*
第一道面试题怎么说呢,面试时候咋就没往简单想亏死了
*/
public class 二叉树根节点到叶子节点的所有路径和 {
public int sumNumbers(TreeNode root) {
// write code here
return fun_DFS(root , 0);
//return fun_BFS(root);
}
public int fun_DFS(TreeNode root , int num){
if (root == null){
return 0;
}
//num用来记录当前路到目前位置的和
num = num*10 + root.val;
//到叶子节点了当前路的值都计算完保存在num返回当前值(这个值num)
if (root.left == null && root.right == null){
return num;
}else{
return fun_DFS(root.left , num) + fun_DFS(root.right , num);
}
}
public int fun_BFS (TreeNode root) {
if (root == null){
return 0;
}
int num = 0;
// 定义两个队列,一个存放当前节点,一个存放截止当前节点是num的值
Queue<TreeNode> queNd = new ArrayDeque<>();
Queue<Integer> queVal = new ArrayDeque<>();
//初始化
queNd.offer(root);
queVal.offer(root.val);
//队列操作
while (!queNd.isEmpty()){
TreeNode cur = queNd.poll();
//val值是截止当前节点之前之前,所在路的和,这很重要千万不要从cur.val取值
int val = queVal.poll();
if (cur.left == null && cur.right == null){
num = num + val;
}
if (cur.left != null){
queNd.offer(cur.left);
queVal.offer(val * 10 + cur.left.val);
}
if (cur.right != null){
queNd.offer(cur.right);
queVal.offer(val * 10 + cur.right.val);
}
}
return num;
}
//题目给定二叉树
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
}
}
《员工的重要性》:题目地址
package 回溯;
//https://leetcode.cn/problems/employee-importance/submissions/
import java.util.*;
public class 员工的重要性 {
int num = 0;//辅助dfs方法记录结果
public int getImportance(List<Employee> employees, int id) {
return fun_DFS(employees , id);
//return fun_BFS(employees , id);
}
//DFS方法也可以用hashMap进行操作
public int fun_DFS(List<Employee> employees, int id) {
for (Employee emp : employees){
if (emp.id == id){
List<Integer> sub = emp.subordinates;
int imp = emp.importance;
num += imp;
for (int i = 0 ; i < sub.size() ; i ++ ){
fun_DFS(employees , sub.get(i));
}
}
}
return num;
}
public int fun_BFS(List<Employee> employees, int id){
Map<Integer , Employee> map = new HashMap<>();
for (Employee emp : employees){
int eid = emp.id;
map.put(eid , emp);
}
int num = 0;
Queue<Employee> que = new ArrayDeque();
que.offer(map.get(id));
while(!que.isEmpty()){
Employee emp = que.poll();
num += emp.importance;
List<Integer> sub = emp.subordinates;
for (int eid : sub){
que.offer(map.get(eid));
}
}
return num;
}
class Employee {
public int id;
public int importance;
public List<Integer> subordinates;
}
}
《图像渲染》:题目地址
这题嗯俺也不知道该不该纠结,我也很菜,他老是内存不够和栈溢出但是嗯可能细节没考虑好吧,建议理解思想就好,不要纠结细节
import java.util.ArrayDeque;
import java.util.LinkedList;
import java.util.Queue;
//https://leetcode.cn/problems/flood-fill/
public class 图像渲染 {
// fun_DFS_2、funBFS辅助处理
int[] x = new int[]{-1 , 0 , 0 , 1};
int[] y = new int[]{0 , -1 , 1 , 0};
public int[][] floodFill(int[][] image, int sr, int sc, int color) {
/*
if (image[sr][sc] != color)
//这个是防止特殊情况的,按理不该这样,但是会栈溢出
fun_DFS(image , sr , sc , image[sr][sc] , color);
return image;
*/
/*
if (image[sr][sc] != color){
//这个是防止特殊情况的,按理不该这样,但是会栈溢出
return fun_DFS_1(image , sr , sc , color);
}
return image;
*/
if (image[sr][sc] != color){
//这个是防止特殊情况的,按理不该这样,但是会栈溢出
fun_BFS(image , sr , sc , color);
}
return image;
}
public int[][] fun_DFS_1(int[][] image, int sr, int sc, int color) {
int old = image[sr][sc];
image[sr][sc] = color;
if (sr - 1 >= 0 && image[sr - 1][sc] == old){
fun_DFS_1(image , sr - 1 , sc , color);
}
if (sr + 1 < image.length && image[sr + 1][sc] == old){
fun_DFS_1(image , sr + 1 , sc , color);
}
if (sc - 1 >= 0 && image[sr][sc - 1] == old){
fun_DFS_1(image , sr , sc - 1 , color);
}
if (sc + 1 < image[sr].length && image[sr][sc + 1] == old){
fun_DFS_1(image , sr , sc + 1 , color);
}
return image;
}
public void fun_DFS_2(int[][] image, int sr, int sc, int old , int color) {
if (image[sr][sc] == old){
image[sr][sc] = color;
for (int i = 0 ; i < 4 ; i++){
int srx = sr + x[i];
int scy = sc + y[i];
if (srx >= 0 && srx < image.length && scy >= 0 && scy < image[0].length){
fun_DFS_2(image , srx , scy , old , color);
}
}
}
}
public int[][] fun_BFS(int[][] image, int sr, int sc, int newColor) {
int old = image[sr][sc];
if (old == newColor) {
return image;
}
int n = image.length, m = image[0].length;
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[]{sr, sc});
image[sr][sc] = newColor;
while (!queue.isEmpty()) {
int[] cell = queue.poll();
int srx = cell[0], scy = cell[1];
for (int i = 0; i < 4; i++) {
int mx = srx + x[i], my = scy + y[i];
if (mx >= 0 && mx < n && my >= 0 && my < m && image[mx][my] == old) {
queue.offer(new int[]{mx, my});
image[mx][my] = newColor;
}
}
}
return image;
}
}
《岛屿的周长》:题目地址
//https://leetcode.cn/problems/island-perimeter/
public class 岛屿的周长 {
//BFS辅助处理
int[] x = new int[]{1 , -1 , 0 , 0};
int[] y = new int[]{0 , 0 , 1 , -1};
public int islandPerimeter(int[][] grid) {
for (int i = 0 ; i < grid.length ; i++){
for (int j = 0 ; j < grid[0].length ; j++){
if (grid[i][j] == 1){
return fun_DFS(grid , i , j);
}
}
}
return 0;
//return fun_BFS(grid);
}
public int fun_DFS(int[][] grid , int gx , int gy){
//这道题的目的是计算边长边长边长不是岛屿
if (gx < 0 || gx >= grid.length || gy < 0 || gy >= grid[0].length){
//当前岛屿不符合要求,说明岛屿这边空了不用递归了,给他的边长加一
return 1;
}
if (grid[gx][gy] == 0){
//岛屿这边是海洋,不符合条件,不用递归了,给岛屿边长加一
return 1;
}
if (grid[gx][gy] != 1){
//这个已经判断过了,他的上下左右边长都已经经过处理了直接返回
return 0;
}
grid[gx][gy] = 2;
return fun_DFS(grid , gx - 1 , gy) + fun_DFS(grid , gx + 1 , gy) + fun_DFS(grid , gx , gy - 1) + fun_DFS(grid , gx , gy + 1);
}
public int fun_BFS(int[][] grid){
//BFS在遍历时只会找其周围四个格子的情况,不用考虑重复,每次循环遇到陆地就遍历其周围四个方向
int real = 0;
for (int i = 0 ; i < grid.length ; i++){
for (int j = 0 ; j < grid[0].length ; j++){
if (grid[i][j] == 1){
int num = 0;
for (int k = 0; k < 4; k++) {
int m = i + x[k];
int n = j + y[k];
if (m < 0 || m >= grid.length || n < 0 || n >= grid[0].length || grid[m][n] == 0){
num += 1;
}
}
real += num;
}
}
}
return real;
}
}
《被围绕的区域》:题目地址
本题的BFS写法有待优化理解思想、有兴趣的话可以尝试一下
//https://leetcode.cn/problems/surrounded-regions/
import sun.misc.Queue;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;
public class 被围绕的区域 {
int[] x = new int[]{-1 , 0 , 0 , 1};
int[] y = new int[]{0 , -1 , 1 , 0};
public void solve(char[][] board) {
//先处理边界,如果边界是o,与o相连的肯定不是被包围的,处理完之后剩下的o就是被包围的
for (int i = 0 ; i < board.length ; i++){
if (board[i][0] == 'O'){
fun_DFS(board , i , 0);
}
if (board[i][board[0].length - 1] == 'O'){
fun_DFS(board , i , board[0].length - 1);
}
}
for (int i = 0 ; i < board[0].length ; i++){
if (board[0][i] == 'O'){
fun_DFS(board , 0 , i);
}
if (board[board.length - 1][i] == 'O'){
fun_DFS(board , board.length - 1 , i);
}
}
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] == 'O'){
board[i][j] = 'X';
}
if (board[i][j] == 'Z'){
board[i][j] = 'O';
}
}
}
}
public void fun_DFS(char[][] board , int gx , int gy){
if (board[gx][gy] == 'O'){
board[gx][gy] = 'Z';
}
for (int i = 0 ; i < 4 ; i ++){
int nx = gx + x[i];
int ny = gy + y[i];
if (nx >= 0 && nx < board.length && ny >= 0 && ny < board[0].length && board[nx][ny] == 'O'){
fun_DFS(board , nx , ny);
}
}
}
public void fun_BFS(char[][] board , int gx , int gy){
//这里没优化,在LeetCode上一些数据内存会不够用,可以尝试优化一下
Deque<Point> que = new LinkedList<>();
if (board[gx][gy] == 'O'){
board[gx][gy] = 'Z';
}
que.offer(new Point(gx , gy));
while (!que.isEmpty()){
Point point = que.poll();
for (int i = 0 ; i < 4 ; i ++){
int nx = point.x + x[i];
int ny = point.y + y[i];
if (nx >= 0 && nx < board.length && ny >= 0 && ny < board[0].length && board[nx][ny] == 'O'){
que.offer(new Point(nx , ny));
}
}
}
}
//辅助BFS的点类
public class Point{
int x;
int y;
public Point() {
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
}
《岛屿数量》:题目地址
同上题一样BFS解时,遇到大的极端数据,内存不够用,有待优化
import java.util.ArrayDeque;
import java.util.Deque;
//https://leetcode.cn/problems/number-of-islands/
public class 岛屿数量 {
int[] x = new int[]{-1 , 0 , 0 , 1};
int[] y = new int[]{0 , -1 , 1 , 0};
int n = 0;
public int numIslands(char[][] grid) {
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == '1'){
fun_DFS(grid , i , j);
n ++;
}
}
}
return n;
}
public void fun_DFS(char[][] grid , int fx , int fy){
if (grid[fx][fy] == '1'){
grid[fx][fy] = '2';
for (int i = 0 ; i < 4 ; i++){
int nx = fx + x[i];
int ny = fy + y[i];
if (nx >= 0 && nx < grid.length && ny >= 0 && ny < grid[0].length && grid[nx][ny] == '1'){
fun_DFS(grid , nx , ny);
}
}
}
}
public void fun_BFS(char[][] grid , int fx , int fy){
Deque<Point> que = new ArrayDeque<>();
que.offer(new Point(fx , fy));
while (!que.isEmpty()){
Point point = que.poll();
if (grid[point.x][point.y] == '1'){
grid[point.x][point.y] = '2';
}
for (int i = 0 ; i < 4 ; i ++){
int nx = point.x + x[i];
int ny = point.y + y[i];
if (nx >= 0 && nx < grid.length && ny >= 0 && ny < grid[0].length && grid[nx][ny] == '1'){
que.offer(new Point(nx , ny));
}
}
}
}
//辅助BFS的点类
public class Point{
int x;
int y;
public Point() {
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
}
有没有感觉到其实现在基本掌握这一类题型了,加油。。。。。。
《岛屿的最大面积》:题目地址
import java.util.ArrayDeque;
import java.util.Deque;
//https://leetcode.cn/problems/max-area-of-island/
/*
求每个岛的长在比较最大,不要往复杂想
*/
public class 岛屿的最大面积 {
int[] x = new int[]{-1, 0, 0, 1};
int[] y = new int[]{0, -1, 1, 0};
int n = 0;
public int maxAreaOfIsland(int[][] grid) {
int m = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == 1) {
m = Math.max(m, fun_DFS(grid, i, j));
// m = Math.max(m, fun_BFS(grid, i, j)); BFS操作
}
}
}
return m;
}
public int fun_DFS(int[][] grid, int gx, int gy) {
if (gx >= 0 && gx < grid.length && gy >= 0 && gy < grid[0].length && grid[gx][gy] == 1) {
grid[gx][gy] = 2;
return 1 + fun_DFS(grid, gx - 1, gy) + fun_DFS(grid, gx + 1, gy) + fun_DFS(grid, gx, gy - 1) + fun_DFS(grid, gx, gy + 1);
}
return 0;
}
public int fun_BFS(int[][] grid, int gx, int gy) {
int n = 0;
Deque<Point> que = new ArrayDeque<>();
que.offer(new Point(gx, gy));
grid[gx][gy] = 2;//入队就操作
while (!que.isEmpty()) {
Point point = que.poll();
n++;
grid[point.x][point.y] = 2;
for (int i = 0; i < 4; i++) {
int nx = point.x + x[i];
int ny = point.y + y[i];
if (nx >= 0 && nx < grid.length && ny >= 0 && ny < grid[0].length && grid[nx][ny] == 1) {
que.offer(new Point(nx, ny));
grid[nx][ny] = 2;//入队时候就将点制为2(操作过)否则对角线位置会重复判断
}
}
}
return n;
}
//辅助BFS的点类
public class Point{
int x;
int y;
public Point() {
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
}
《二进制手表》:题目地址
这道题跟上一道比较像,但是倒不一定一定要回溯来解,俺是参考官解做的所以也没有回溯,主要是写这道题的时候上一道《电话号码的字母组合》俺还没看懂,所以也跟官解一样暴力枚举了
import java.util.ArrayList;
import java.util.List;
//https://leetcode.cn/problems/binary-watch/
public class 二进制手表 {
public List<String> readBinaryWatch(int turnedOn) {
return fun_enumerate_1(turnedOn);
}
public List<String> fun_enumerate_1(int turnedOn) {
List<String> list = new ArrayList<>();
for (int i = 0 ; i < 12 ; i++){
for (int j = 0 ; j < 60 ; j++){
if (Integer.bitCount(i) + Integer.bitCount(j) == turnedOn){
list.add(i + ":" + (j > 10 ? "0" : "") + j);
}
}
}
return list;
}
public List<String> fun_enumerate_2(int turnedOn) {
}
}
是不是以为要下一题了,然鹅并没有,让我们来看看他用回溯该怎么写,因为比较有代表性,所以拿出来专门放一块位置
package 回溯;
import java.util.ArrayList;
import java.util.List;
//https://leetcode.cn/problems/binary-watch/
public class 二进制手表 {
int[] hr = new int[]{1,2,4,8,0,0,0,0,0,0};
int[] mr = new int[]{0,0,0,0,1,2,4,8,16,32};
List<String> list = new ArrayList<>();
public List<String> readBinaryWatch(int turnedOn) {
//return fun_enumerate_1(turnedOn);
fun_DFS(turnedOn , 0 , 0 , 0);
return list;
}
public void fun_DFS(int num , int j , int h , int m){
if (h > 11 || m > 59){
return;
}
//当然,如果num==0的话说明led用完了,而且截止目前h和m也都满足条件,所以时h和m表示的就是时间
if (num == 0){
list.add(h + ":" + (m < 10 ? "0" : "") + m);
return;
}
for (int i = j ; i < 10 ; i++){
fun_DFS(num - 1 , i + 1 , h + hr[i] , m + mr[i]);
}
}
}
《组合总和》:题目地址
附一张LeetCode官解的图很详细可供参考,图注:来源于LeetCode
import java.util.ArrayList;
import java.util.List;
//https://leetcode.cn/problems/combination-sum/
public class 组合总和 {
List<List<Integer>> list = new ArrayList<>();
List<Integer> ail = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
fun_DFS(candidates , target , 0);
return list;
}
public void fun_DFS(int[] candidates , int target , int index){
//当索引与长度相等,很明显这条路走完也没有合适的
if (index == candidates.length){
return;
}
if (target == 0){
//如果值减到0了,说明这条路是正确的把他加进去
list.add(new ArrayList<>(ail));
return;
}
//不用当前索引值查看能否完成跟《二进制手表》有点像,类似与循环时确定一个值,只考虑后面的
fun_DFS(candidates , target , index + 1);
if (target - candidates[index] >= 0){
ail.add(candidates[index]);
fun_DFS(candidates , target - candidates[index] , index);
ail.remove(ail.size() - 1);
}
}
}
《活字印刷》:题目地址
package 回溯;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
//https://leetcode.cn/problems/letter-tile-possibilities/
public class 活字印刷 {
int count = 0;
char[] cha;
public int numTilePossibilities(String tiles) {
cha = tiles.toCharArray();
boolean[] have = new boolean[cha.length];
Arrays.sort(cha);
fun(have);
return count;
}
private void fun(boolean[] have) {
char last = '+';
for (int i = 0; i < cha.length; i++) {
if (!have[i] && cha[i] != last){
count ++;
have[i] = true;
fun( have);
have[i] = false;
last = cha[i];
}
}
}
}
《N 皇后》:题目地址
package 回溯;
import java.util.*;
//https://leetcode.cn/problems/n-queens/
public class N皇后 {
public List<List<String>> solveNQueens(int n) {
List<List<String>> list = new ArrayList<>();
//存放行信息节点,就是每行的皇后都在哪个位置,因为每行只能放一个所以定义n个就好了
// 这里需要初始化一下默认是0;但是0我们要用有第0行列嘛,我们初始化为-1;
int[] cur = new int[n];
//初始化
Arrays.fill(cur , -1);
Set<Integer> row = new HashSet<>();
Set<Integer> lht = new HashSet<>();
Set<Integer> rht = new HashSet<>();
fun(list , cur , n ,0 , row , lht , rht);
return list;
}
private void fun(List<List<String>> list, int[] cur, int n , int dis, Set<Integer> row, Set<Integer> lht, Set<Integer> rht) {
if (dis == n){
List<String> back = backValue(cur , n);
list.add(back);
}else {
for (int i = 0; i < n; i++) {
if (row.contains(i)){
continue;
}
int lhtV = dis - i;
if (lht.contains(lhtV)){
continue;
}
int rhtV = dis + i;
if (rht.contains(rhtV)){
continue;
}
cur[dis] = i;
row.add(i);
lht.add(lhtV);
rht.add(rhtV);
fun(list , cur , n , dis + 1 , row , lht , rht);
cur[dis] = -1;
row.remove(i);
lht.remove(lhtV);
rht.remove(rhtV);
}
}
}
//给之前处理过存有每行皇后所在位置的数组和大小返回成链表
private List<String> backValue(int[] cur, int n) {
List<String> arr = new ArrayList<>();
for (int i = 0; i < n; i++) {
char[] row = new char[n];
Arrays.fill(row , '.');
row[cur[i]] = 'Q';
arr.add(new String(row));
}
return arr;
}
}