这期刷leetcode上所有的回溯专题

93. 复原IP地址

这题先不考虑一些剪枝类问题,只是单纯考虑AC,我们设置的两个参数,一个index表示索引到哪个字符串下标了,另一个strs双端队列放入表示合法的字符串(0-255)

class Solution {int[] segment = new int[4];List<String> res;public List<String> restoreIpAddresses(String s) {res = new ArrayList<>();char[] ch = s.toCharArray();dfs(ch, 0, 0);return res;}public void dfs(char[] ch, int segId, int index){if(segId == 4){if(index == ch.length){StringBuilder sb = new StringBuilder();for(int i = 0; i<4; i++){sb.append(segment[i]);if(i < 3) sb.append(".");}res.add(new String(sb));}return;}if(index == ch.length) return;if(ch[index] == '0'){segment[segId] = 0;dfs(ch, segId+1, index+1);}int sum = 0;for(int i = index; i<ch.length; i++){sum = sum * 10 + ch[i]-'0';if(sum > 0 && sum < 256){segment[segId] = sum;dfs(ch, segId+1, i+1);}else break;}}
}

131. 分割回文串

这里的思路和上题差不多,但是坑比较多,我太菜的原因,先看代码:

class Solution {public List<List<String>> partition(String s) {List<List<String>> res = new ArrayList<>();if(s.length() == 0) return res;helper(s,0,new ArrayList<>(),res);return res;}public boolean isPalindrome(String str){int start = 0;int end = str.length()-1;while(end > start){if(str.charAt(start) != str.charAt(end)) return false;end--;start++;}return true;}public void helper(String s, int index, List<String> r, List<List<String>> res){if(index == s.length()) {res.add(new ArrayList<>(r));  // 这里不能直接res.add(r),否则r改变后,res里面随之改变return;}for(int i = 1; i+index<=s.length(); i++){String choose = s.substring(index,index+i);if(isPalindrome(choose)){r.add(choose);helper(s,index+i,r,res);r.remove(r.size()-1);}else continue;  // 坑二}return;}

坑一: List列表的删除会影响到已经加到res里面的列表
坑二:该位置之前写的return,直接影响到数组后面元素的添加,比如说 aba,我添加到ab时显示的是非回文字符,return之后就没有继续下去考虑aba的情况,所以此处用continue


排列问题,这类问题在面试中会常问到

  1. 全排列
class Solution {List<List<Integer>> res = new ArrayList<>();int[] visited;public List<List<Integer>> permute(int[] nums) {if(nums.length == 0) return res;visited = new int[nums.length];helper(nums, new LinkedList<>(),0);return res;}public void helper(int []num, Deque<Integer> t, int index){if(index == num.length){res.add(new ArrayList<>(t));return ;}if(index > num.length) return;for(int i = 0; i < num.length; i++){if(visited[i] == 1) continue; visited[i] = 1;t.offerLast(num[i]);helper(num,t,index+1);visited[i] = 0;t.pollLast();}return;}
}

组合问题

class Solution {List<List<Integer>> res = new ArrayList<>();public List<List<Integer>> combine(int n, int k) {if(k > n || n == 0 || k == 0) return res;helper(n,k,0,new ArrayList<>());return res;}public void helper(int n, int k, int index, List<Integer>list){if(list.size() == k){res.add(new ArrayList<>(list));return;}for(int i = index; i<n; i++){list.add(i+1);helper(n,k,i+1,list); // i+1 表示用过的元素,后面不用了,这里不能用index+1list.remove(list.size()-1);}return;}
}

全套组合总和问题

组合总和 I

class Solution {List<List<Integer>> res = new ArrayList<>();public List<List<Integer>> combinationSum(int[] candidates, int target) {if(candidates.length == 0) return res;helper(candidates,target,new ArrayList<>(),0,0);return res;}public void helper(int [] candidates, int target, List<Integer> list, int sum, int index){if(sum == target){res.add(new ArrayList<>(list));return;}if(sum > target) return;for(int i = index; i < candidates.length; i++){list.add(candidates[i]);sum += candidates[i];helper(candidates,target,list,sum, i);sum -= candidates[i];list.remove(list.size()-1);}return;}
}

组合总和这个,这个必须消化,这是一个模板了:组合总和 II

class Solution {List<List<Integer>> res = new ArrayList<>();public List<List<Integer>> combinationSum2(int[] candidates, int target) {Arrays.sort(candidates);if(candidates.length == 0) return res;helper(candidates,target,new ArrayList<>(),0,0);return res;}public void helper(int [] candidates, int target, List<Integer> list, int sum, int index){if(sum == target){res.add(new ArrayList<>(list));return;}if(sum > target) return;for(int i = index; i < candidates.length; i++){if(i > index && candidates[i] == candidates[i-1]) continue;list.add(candidates[i]);sum += candidates[i];helper(candidates,target,list,sum, i+1);sum -= candidates[i];list.remove(list.size()-1);}return;}
}

继续延伸到 组合总和 III,将组合问题贯彻到底

class Solution {public List<List<Integer>> res = new ArrayList<>();public List<List<Integer>> combinationSum3(int k, int n) {if(k == 0 || n < 1) return res;helper(k,n,new ArrayList<>(),0,1);return res;}public void helper(int k, int n, List<Integer> list, int sum, int index){if(k == list.size() && sum == n){res.add(new ArrayList<>(list));return;}if(sum > n) return;if(index > 9) return;for(int i = index; i<=9; i++){sum += i;list.add(i);helper(k,n,list,sum,i+1);sum -= i;list.remove(list.size()-1);}return;}
}

组合问题 IV(超时了,需要剪枝)

class Solution {int res = 0;public int combinationSum4(int[] nums, int target) {if(nums.length == 0) return res;helper(nums, target, 0);return res;}public void helper(int []nums, int target, int sum){if(sum == target){res += 1;return;}if(sum > target) return;for(int i = 0; i<nums.length; i++){sum += nums[i];if(sum > target) return;helper(nums,target,sum);sum -= nums[i];}return;}
}

记忆化搜索AC:

class Solution {int[] memo;public int combinationSum4(int[] nums, int target) {int len = nums.length;if(len == 0) return 0;memo = new int[target+1];Arrays.fill(memo, -1);return helper(nums, target);}public int helper(int[] nums, int target){if(target < 0) return 0;if(target == 0) return 1;if(memo[target] != -1) return memo[target];int sum = 0;for(int i = 0; i<nums.length; i++) sum += helper(nums, target-nums[i]);memo[target]= sum;return sum;}
}

这里再补充一个 用背包解决的,这里显然是一个完全背包问题:


357. 计算各个位数不同的数字个数


这道题目,我看题解大部分都是用动态规划做的,但是由于是回溯专题,强行用回溯做了 ,结果可想而知:

class Solution {int res = 0;public int countNumbersWithUniqueDigits(int n) {if(n == 0) return 1;if(n == 1) return 10;helper(n, new ArrayList<>());return res;}public void helper(int n, List<Integer> list){if(list.size() > 1 && list.get(0) == 0) return;if(list.size() > n) return;if(list.size() > 0){res += 1;  // 这里有一个坑,习惯性在后面写个return; 结果直接不考虑后面的情况,只输出一位数的情况}for(int i = 0; i < 10; i++){if(list.contains(i)) continue;list.add(i);helper(n, list);list.remove(list.size()-1);}return;}
}

子集 II


class Solution {public List<List<Integer>> res = new ArrayList<>();public List<List<Integer>> subsetsWithDup(int[] nums) {if(nums.length == 0) return res;helper(nums, 0, new ArrayList<>(),new HashMap<>());return res;}public void helper(int[] nums, int index, List<Integer> list, HashMap<List<Integer>,Integer> map){if(index > nums.length) return;if(list.size() > nums.length) return;List<Integer> temp = new ArrayList<>(list);Collections.sort(temp);if(map.get(temp) == null){res.add(new ArrayList<>(temp));map.put(temp,0);}for(int i = index; i<nums.length; i++){list.add(nums[i]);helper(nums, i+1, list, map);list.remove(list.size()-1);}return;}
}

二维平面中的回溯算法

单词搜索

class Solution {public boolean exist(char[][] board, String word) {if(word.length() == 0) return false;for(int i = 0; i<board.length; i++){for(int j = 0; j<board[0].length; j++){if(helper(board,word,i,j,0)) return true;}}return false;}public boolean helper(char[][] board, String word, int x, int y, int index){if(index == word.length()){  // 没有考虑边界问题,看下面代码return true;}if(board[x][y] != word.charAt(index)) return false;board[x][y] = '1';  // 这个相当于以前常用的 visited[] = true,表明这个点已经被访问过了if(x+1<board.length && helper(board, word, x+1, y, index+1)) return true;if(x-1>=0 && helper(board, word, x-1, y, index+1)) return true;if(y-1>=0 && helper(board, word, x, y-1, index+1)) return true;if(y+1<board[0].length && helper(board, word, x, y+1, index+1)) return true;board[x][y] = word.charAt(index);  // 还原return false;}
}

上面的代码没有考虑到边界问题,碰到一个元素的情况,就gg了,改成下面这种:

class Solution {public boolean exist(char[][] board, String word) {if(word.length() == 0) return false;for(int i = 0; i<board.length; i++){for(int j = 0; j<board[0].length; j++){if(helper(board,word,i,j,0)) return true;}}return false;}public boolean helper(char[][] board, String word, int x, int y, int index){if(index == word.length()-1){  // 这里是重点return board[x][y] == word.charAt(index);}if(board[x][y] != word.charAt(index)) return false;board[x][y] = '1';if(x+1<board.length && helper(board, word, x+1, y, index+1)) return true;if(x-1>=0 && helper(board, word, x-1, y, index+1)) return true;if(y-1>=0 && helper(board, word, x, y-1, index+1)) return true;if(y+1<board[0].length && helper(board, word, x, y+1, index+1)) return true;board[x][y] = word.charAt(index);return false;}
}

接下来是floodfill类型的题目,也是二维平面的回溯:

岛屿数量

class Solution {int res = 0;int [][]direct = {{0,1},{0,-1},{1,0},{-1,0}};public int numIslands(char[][] grid) {if(grid.length == 0) return 0;for(int i = 0; i<grid.length; i++){for(int j = 0; j<grid[0].length; j++){if(grid[i][j] == '1'){res ++;helper(grid,i,j);}}}return res;}public void helper(char[][] grid, int x, int y){grid[x][y] = '2';   // 不用 visited数组,节省空间for(int i = 0; i<4; i++){int new_x = x + direct[i][0];int new_y = y + direct[i][1];if(inArea(new_x,new_y,grid) && grid[new_x][new_y] == '1'){helper(grid,new_x,new_y);}}}public boolean inArea(int x, int y, char[][] grid){if(x<grid.length && x>=0 && y<grid[0].length && y>=0) return true;return false;}
}

130. 被围绕的区域

class Solution {int [][] direct = {{1,0},{-1,0},{0,1},{0,-1}};int [][]visited;public void solve(char[][] board) {if(board.length == 0 || board[0].length == 0) return;visited = new int[board.length][board[0].length];for(int i = 0; i<board.length; i++){if(board[i][0] == 'O' && visited[i][0] == 0)helper(board,i,0);if(board[i][board[0].length-1] == 'O' && visited[i][board[0].length-1] == 0)helper(board,i,board[0].length-1);}for(int i = 0; i<board[0].length; i++){if(board[0][i] == 'O' && visited[0][i] == 0)helper(board,0,i);if(board[board.length-1][i] == 'O' && visited[board.length-1][i] == 0)helper(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] == 'B') board[i][j] = 'O';}}public void helper(char[][] board, int x, int y){visited[x][y] = 1;board[x][y] = 'B';for(int i = 0; i<4; i++){int new_x = x + direct[i][0];int new_y = y + direct[i][1];if(inArea(new_x,new_y, board) && board[new_x][new_y] == 'O' && visited[new_x][new_y] == 0)helper(board,new_x,new_y);}}public boolean inArea(int x, int y, char[][] board){if(x >= 0 && x < board.length && y >= 0 && y < board[0].length) return true;else return false;}
}

思想: 首先对所有的边界为 “O” 的坐标进行遍历,对O位置以及与O位置相连的坐标全都标注为“B”,对于剩下的坐标为“O”
的,肯定是被包围的,全都设为“X”,再把之前标注为B的,全都还原为O


417. 太平洋大西洋水流问题

class Solution {public List<List<Integer>> pacificAtlantic(int[][] matrix) {List<List<Integer>> res = new ArrayList<>();if(matrix == null || matrix.length == 0){return res;}for(int i=0; i<matrix.length; i++){for(int j=0; j<matrix[0].length; j++){if(dfsPacific(matrix,i,j,Long.MAX_VALUE) && dfsAtlantic(matrix,i,j,Long.MAX_VALUE)){res.add(Arrays.asList(i,j));}}}return res;}//“太平洋”处于大陆的左边界和上边界private boolean dfsPacific(int[][] matrix, int i, int j, long prev){if(i > matrix.length-1 || j > matrix[0].length-1 || matrix[i][j] == -1 || matrix[i][j] > prev){return false;}//到了太平洋的边界,说明可以流入,返回trueif(i <= 0 || j <= 0){return true;}//temp临时记录当前matrix[i][j]的值,返回重复遍历int temp = matrix[i][j];matrix[i][j] = -1;boolean result = dfsPacific(matrix,i-1,j,temp)|| dfsPacific(matrix,i+1,j,temp)|| dfsPacific(matrix,i,j-1,temp)|| dfsPacific(matrix,i,j+1,temp);matrix[i][j] = temp;return result;}//“大西洋”处于大陆的右边界和下边界private boolean dfsAtlantic(int[][] matrix, int i, int j, long prev){if(i < 0 || j < 0 || matrix[i][j] == -1 ||  matrix[i][j] > prev){return false;}//到了大西洋的边界,说明可以流入,返回trueif(i >= matrix.length-1 || j >= matrix[0].length-1){return true;}//temp临时记录当前matrix[i][j]的值,返回重复遍历int temp = matrix[i][j];matrix[i][j] = -1;boolean result = dfsAtlantic(matrix,i-1,j,temp)|| dfsAtlantic(matrix,i+1,j,temp)|| dfsAtlantic(matrix,i,j-1,temp)|| dfsAtlantic(matrix,i,j+1,temp);matrix[i][j] = temp;return result;}
}作者:YanShaoJiangHu
链接:https://leetcode-cn.com/problems/pacific-atlantic-water-flow/solution/shen-du-you-xian-sou-suo-jian-dan-yi-dong-by-yansh/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这个问题我的几次答案都是stack溢出,题解里面好多题主提到了上面的floodfill方法,需要反复试验一下



这道题wr一直没找到原因,直到在lc评论区看到,原来少了点什么

class Solution {List<List<Integer>> res = new ArrayList<>();public List<List<Integer>> pathSum(TreeNode root, int sum) {if(root == null) return res;helper(root, sum, new ArrayList<>());return res;}public void helper(TreeNode root, int sum, List<Integer> list){if(root == null) return;if(root.left == null && root.right == null && root.val == sum){list.add(root.val);res.add(new ArrayList<>(list));list.remove(list.size()-1); // 这里一定要注意,一定要在return之前要删除之前加进的元素return;}list.add(root.val);helper(root.left, sum-root.val, list);helper(root.right, sum-root.val, list);list.remove(list.size()-1);   // 这里也要return;}
}

回溯专题leetcode相关推荐

  1. [DFS|回溯法] leetcode 17 电话号码的字母组合

    [DFS|回溯法] leetcode 17 电话号码的字母组合 1.题目 题目链接 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合. 给出数字到字母的映射如下(与电话按键相同).注意 ...

  2. LintCode 802. 数独(回溯)/ LeetCode 37. 解数独

    1. 题目 编写一个程序,通过填充空单元来解决数独难题. 空单元由数字0表示. 你可以认为只有一个唯一的解决方案. LeetCode 37 题类似,把 int 改成 char,注意转换 2. 解题 行 ...

  3. 字符串专题-LeetCode:剑指 Offer 58 - II. 左旋转字符串、LeetCode 459.重复的子字符串、 代码思路和注意点

    文章目录 一.剑指 Offer 58 - II. 左旋转字符串 二.LeetCode 459.重复的子字符串 一.剑指 Offer 58 - II. 左旋转字符串 思路: 预留出n个字符空间s.res ...

  4. 【回溯专题】—— 回溯算法入门篇

    一.什么是回溯法?

  5. 回溯模板+leetcode——78. 子集 + 90. 子集 II

    回溯法 一般情况下,看到题目要求「所有可能的结果」,而不是「结果的个数」,我们就知道需要暴力搜索所有的可行解了,可以用「回溯法」. 「回溯法」实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻 ...

  6. 【代码随想录】-回溯专题

    文章目录 基础理论 组合 组合总和3 电话号码的字母组合 组合总和 组合总和2 分割回文串 复原ip地址 子集问题 子集2 递增子序列 全排列 全排列2 重新安排行程 N皇后 解数独 基础理论 回溯算 ...

  7. 回溯专题——leetcode47. Permutations II medium(有重复数字的全排列)

    1.题目描述 Given a collection of numbers, nums, that might contain duplicates, return all possible uniqu ...

  8. 寒假LeetCode打卡

    文章目录 @[toc] 链表专题 LeetCode 19. Remove Nth Node From End of List LeetCode 83. Remove Duplicates from S ...

  9. LeetCode专题:树与回溯(完结,已更50题)

    目录 LeetCode二叉树的基本遍历(难): 写在前面: 前序遍历: Morris遍历: 中序遍历: Morris遍历: 后序遍历: Morris遍历: 二叉树前中后迭代方式同一写法: 鸣谢: Le ...

最新文章

  1. 训练集(train set) 验证集(validation set) 测试集(test set)
  2. java对象转json字符串日期格式_fastJSON字符串类型数据中的日期转换为Java bean的日期对象...
  3. 实践Jenkins集成Cobertura自动化构建SpringBoot工程
  4. S/4HANA for Customer Management里的搜索分页处理
  5. Docker安装+镜像拉取+容器+创建镜像+push to docker hub
  6. discuz 服务器维护,论坛服务器经常宕机 - Discuz!-安装使用 - Discuz! 官方站 - Powered by Discuz!...
  7. java运行 mavenzip包_JAVA开发必知工具-Maven-Maven生成可以直接运行的jar包的多种方式...
  8. 免费易用的Web版OFD阅读器
  9. 华中邀请四届 WHU1567 Sloth's Angry
  10. 保姆级笔记-佳能Canon LBP2900在Win10安装驱动
  11. 常用计算机 启动bios,电脑进入BIOS的方法集合
  12. 她在IT圈里摸爬滚打的十年
  13. 2018年sfdc工作总结_Lightning公用组件_模糊搜索单选组件
  14. 微信小程序canvas绘制圆形头像
  15. 扫描图片怎么变成电子版?不止扫描仪
  16. 支付宝扫福最佳攻略,你值得拥有
  17. 软件测试面试前必备题库(必备理论基础复习)
  18. C语言写一个函数,输入一行字符,将此字符串中最长的单词输出
  19. linux中kvm配置文件,如何在linux中通过kvm安装虚拟机
  20. 直接引用vue.js,也可以出效果。但是这样的利弊在哪?搭建脚手架的利弊又是什么呢?

热门文章

  1. 算法笔记 --- 排列组合
  2. 设置屏幕分辨率的函数 - 回复 董勇 的问题
  3. 《CLR via C#》读书笔记 之 基元类型、引用类型和值类型
  4. apache的php扩展名解析漏洞
  5. 协议编码分析 - ARP协议详解
  6. EMC业务连续性和容灾服务
  7. zt:tcpdump抓包对性能的影响
  8. 如何在新版的gitbook上写自己的书
  9. 强上阿里云之安装MYSQL
  10. 《MapReduce 2.0源码分析与编程实战》一第2章 入门