模板

result = {}
void backtrack(选择列表, 路径) {
if (满足结束条件) {
result.add(路径)
return
} for 选择 in 选择列表 {
做选择
backtrack(选择列表,路径)
撤销选择
}
}

核心就是从选择列表里做一个选择,然后一直递归往下搜索答案,如果遇到路径不通,就返回来撤销这次选择。

推荐阅读回溯思想团灭排列、组合、子集问题

78. 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

class Solution {
public List<list<integer>> subsets(int[] nums) {
LinkedList<integer> track = new LinkedList<>();
dfs(nums, track, 0);
return res;
} private List<list<integer>> res = new LinkedList<>();
private void dfs (int[] nums, LinkedList<integer> track, int start) {
res.add(new LinkedList<>(track)); //通过start来控制当前可以选择的列表
for (int i = start; i < nums.length; i++) {
//选择
track.offerLast(nums[i]);
dfs(nums, track, i + 1);
//撤销选择
track.pollLast();
}
}
} // {1, 2, 3}对应递归树:
// []
// 1 2 3
// 12 13 23
// 123

90. 子集 II

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

class Solution {
public List<list<integer>> subsetsWithDup(int[] nums) {
LinkedList<integer> track = new LinkedList<>();
//先排序
Arrays.sort(nums);
dfs(nums, track, 0);
return res;
} private List<list<integer>> res = new LinkedList<>(); private void dfs(int[] nums, LinkedList<integer> track, int start) {
res.add(new LinkedList<>(track)); for (int i = start; i < nums.length; i++) {
//剪枝(跳过重复的)
if ((i != start) && (nums[i] == nums[i - 1])) {
continue;
} track.offerLast(nums[i]);
dfs(nums, track, i + 1);
track.pollLast();
}
}
} // {1, 2, 2, 3}对应递归树:
// []
// 1 2 2(剪) 3
// 12 12(剪) 13 22 23 23
// 123 123 223

46. 全排列

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

class Solution {
public List<list<integer>> permute(int[] nums) {
LinkedList<integer> track = new LinkedList<>();
boolean[] visited = new boolean[nums.length]; dfs (nums, track, visited); return res;
} private List<list<integer>> res = new LinkedList<>();
private void dfs(int[] nums, LinkedList<integer> track, boolean[] visited) {
// base case
if (track.size() == nums.length) {
res.add(new LinkedList<integer>(track));
return;
} for (int i = 0; i < nums.length; i++) {
// track中已经包含的就跳过
if (visited[i]) {
continue;
} // 选择
track.offerLast(nums[i]);
visited[i] = true; dfs(nums, track, visited); // 撤销选择
track.pollLast();
visited[i] = false;
}
}
}

47. 全排列 II

给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

class Solution {
public List<list<integer>> permuteUnique(int[] nums) {
LinkedList<integer> track = new LinkedList<>();
boolean[] visited = new boolean[nums.length];
Arrays.sort(nums); dfs (nums, track, visited); return res;
} private List<list<integer>> res = new LinkedList<>();
private void dfs(int[] nums, LinkedList<integer> track, boolean[] visited) {
// base case
if (track.size() == nums.length) {
res.add(new LinkedList<integer>(track));
return;
} for (int i = 0; i < nums.length; i++) {
// track中已经包含的就跳过
if (visited[i]) {
continue;
} // 剪枝
if (i != 0 && nums[i] == nums[i - 1] && !visited[i - 1]) {
continue;
} // 选择
track.offerLast(nums[i]);
visited[i] = true; dfs(nums, track, visited); // 撤销选择
track.pollLast();
visited[i] = false;
}
}
}

推荐阅读回溯搜索 + 剪枝

39. 组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。
class Solution {
public List<list<integer>> combinationSum(int[] candidates, int target) {
LinkedList<integer> track = new LinkedList<>();
dfs(candidates, track, target, 0);
return res;
} private List<list<integer>> res = new LinkedList<>(); private void dfs(int[] candidates, LinkedList<integer> track, int target, int start) {
//base case
if (target < 0) {
return;
}
//base case
if (target == 0) {
res.add(new LinkedList<>(track));
return;
} //通过start改变可选列表
for (int i = start; i < candidates.length; i++) {
track.offerLast(candidates[i]);
dfs(candidates, track, target - candidates[i], i);
track.pollLast();
}
}
}

推荐阅读:回溯算法 + 剪枝(回溯经典例题详解)

class Solution {
public List<list<integer>> combinationSum(int[] candidates, int target) {
LinkedList<integer> track = new LinkedList<>();
Arrays.sort(candidates); //排序后可实现进一步剪枝
dfs(candidates, track, target, 0);
return res;
} private List<list<integer>> res = new LinkedList<>(); private void dfs(int[] candidates, LinkedList<integer> track, int target, int start) {
//不会再出现target小于0的情况
//base case
if (target == 0) {
res.add(new LinkedList<>(track));
return;
} //通过start改变可选列表
for (int i = start; i < candidates.length; i++) {
//由于此时candidates是有序的,candidates[i] 后面的元素都大于target,直接忽略
if (target - candidates[i] < 0) {
break;
} track.offerLast(candidates[i]);
dfs(candidates, track, target - candidates[i], i);
track.pollLast();
}
}
}

17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

class Solution {
public List<string> letterCombinations(String digits) {
if (digits == null || digits.length() <= 0) {
return new LinkedList<string>();
} StringBuilder track = new StringBuilder();
dfs(digits, 0, track);
return res;
} private Map<character, string[]=""> map = new HashMap<>() {
{
put('2', new String[] {"a", "b", "c"});
put('3', new String[] {"d", "e", "f"});
put('4', new String[] {"g", "h", "i"});
put('5', new String[] {"j", "k", "l"});
put('6', new String[] {"m", "n", "o"});
put('7', new String[] {"p", "q", "r", "s"});
put('8', new String[] {"t", "u", "v"});
put('9', new String[] {"w", "x", "y", "z"});
}
}; private List<string> res = new LinkedList<>();
private void dfs(String digits, int index, StringBuilder track) {
if (index == digits.length()) {
res.add(track.toString());
return;
} String[] alphabets = map.get(digits.charAt(index));
for (int i = 0; i < alphabets.length; i++) {
// 选择
track.append(alphabets[i]); dfs(digits, index + 1, track); // 撤销选择
track.deleteCharAt(track.length() - 1);
}
} }

131. 分割回文串

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

回文串 是正着读和反着读都一样的字符串。

class Solution {
public List<list<string>> partition(String s) { // 先使用动态规划获得任意两个区间的字符串是否为回文字符串
boolean[][] isPalindrome = getPalindrome(s); LinkedList<string> track = new LinkedList<>();
dfs(s, 0, track, isPalindrome); return res; } private List<list<string>> res = new LinkedList<>();
private boolean[][] getPalindrome(String s) {
int len = s.length(); // 区间i,j的字符串是否为回文字符串(左右都为闭区间)
boolean[][] dp = new boolean[len][len]; for (int j = 0; j < len; j++) {
for (int i = 0; i <= j; i++) {
if (s.charAt(i) == s.charAt(j) && (j - i <= 2 || dp[i + 1][j - 1])) {
dp[i][j] = true;
}
}
} return dp;
} private void dfs(String s, int start, LinkedList<string> track, boolean[][] isPalindrome) {
if (start == s.length()) {
res.add(new LinkedList<>(track));
return;
} for (int i = start; i < s.length(); i++) {
// 如果区间[start, i]的字符串不为回文字符串就可以剪枝
if (!isPalindrome[start][i]) {
continue;
} // 选择
track.offerLast(s.substring(start, i + 1)); dfs(s, i + 1, track, isPalindrome); // 撤销选择
track.pollLast();
}
}
}

93. 复原 IP 地址

给定一个只包含数字的字符串,用以表示一个 IP 地址,返回所有可能从 s 获得的 有效 IP 地址 。你可以按任何顺序返回答案。

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。

class Solution {
public List<string> restoreIpAddresses(String s) {
int len = s.length();
if (len < 4 || len > 12) return new LinkedList<>(); LinkedList<string> track = new LinkedList<>();
dfs(s, 0 ,track); return res;
} private List<string> res = new LinkedList<>();
private void dfs(String s, int start, LinkedList<string> track) {
// 只有track已经有4段,且没有剩余字符时符合条件
if (track.size() == 4) {
if (start == s.length()) {
res.add(toStr(track));
} return;
} for (int i = start; i < start + 3 && i < s.length(); i++) {
String buf = s.substring(start, i + 1); if (!isValid(buf)) {
continue;
} track.offerLast(buf); dfs(s, i + 1, track); track.pollLast();
}
} private String toStr(LinkedList<string> list) {
return String.join(".", list);
} private boolean isValid(String str) {
int len = str.length();
int buf = Integer.parseInt(str); // 排除 09 等
if (len == 2 && buf < 10) {
return false;
// 排除 099、256 等
} else if (len == 3 && (buf < 100 || buf > 255)) {
return false;
} return true;
}
}

LeetCode入门指南 之 回溯思想的更多相关文章

  1. LeetCode入门指南 之 动态规划思想

    推荐学习labuladong大佬的动态规划系列文章:先弄明白什么是动态规划即可,不必一次看完.接着尝试自己做,没有思路了再回过头看相应的文章. 动态规划一般可以由 递归 + 备忘录 一步步转换而来,不 ...

  2. LeetCode入门指南 之 链表

    83. 删除排序链表中的重复元素 存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 .返回同样按升序排列的结果链表. class Soluti ...

  3. LeetCode入门指南 之 排序

    912. 排序数组 给你一个整数数组 nums,请你将该数组升序排列. 归并排序 public class Sort { //归并排序 public static int[] MergeSort(in ...

  4. LeetCode入门指南 之 二叉树

    二叉树的遍历 递归: void traverse (TreeNode root) { if (root == null) { return null; } //前序遍历位置 traverse(root ...

  5. LeetCode入门指南 之 栈和队列

    栈 155. 最小栈 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈. push(x) -- 将元素 x 推入栈中. pop() -- 删除栈顶的元素. top( ...

  6. LeetCode入门指南 之 二分搜索

    上图表示常用的二分查找模板: 第一种是最基础的,查找区间左右都为闭区间,比较后若不等,剩余区间都不会再包含mid:一般在不需要确定目标值的边界时,用此法即可. 第二种查找区间为左闭右开,要确定targ ...

  7. Ext JS 6学习文档–第1章–ExtJS入门指南

    Ext JS 入门指南 前言 本来我是打算自己写一个系列的 ExtJS 6 学习笔记的,因为 ExtJS 6 目前的中文学习资料还很少.google 搜索资料时找到了一本国外牛人写的关于 ExtJS ...

  8. 【翻译Autofac的帮助文档】1.入门指南

    [写在前面]尝试做完一件工作之外自我觉得有意义的一件事,那就从翻译Autofac的帮助文档吧. 入门指南 将Autofac集成你的应用程序的步骤通常很简单,一般是: 时刻以IOC(控制反转)的思想来规 ...

  9. OpenCASCADE入门指南

    OpenCASCADE入门指南 eryar@163.com 一.概述 荀子说“君子性非异也,善假于物也”.当你会用英语,就可以与世界各国的人交流:当你会用编程语言,就可以与计算机交流:当你会用数学语言 ...

随机推荐

  1. xampp中修改mysql默认空密码

    打卡记录: 1. mysql用户的相关信息是保存在mysql数据库的user表中的,并且该表的密码字段(Password)是通过PASSWORD方法加密存储的,所以不能直接修改成123456. 2. ...

  2. Windows API 简介

    操作系统的作用之一就是屏蔽一些复杂的直接对硬件操作,并提供给用户一个简单明确的应用接口,类外对于一些基本的或常用的操作也以API的形式提供给用户,比如内存管理.文件管理等. 消息传递机制 消息循环是一 ...

  3. 手写RPC

    服务端代码 package com.peiyu.rpcs.bios; import java.io.IOException; public interface IRpcServers { void s ...

  4. 17Java进阶——反射、进程、Java11新特性

    1.Java反射机制 Java反射(Reflection)概念:在运行时动态获取类的信息以及动态调用对象方法的功能. 1.1反射的应用--通过全类名获取类对象及其方法 package two.refl ...

  5. layui 页面加载完成后ajax重新为 html 赋值 遇到的坑

    页面加载完毕后,通过 ajax 按照返回值,为部分 html 赋值: $(function(){ ..... }) 直接这样写,报错,$ 没有定义什么的,错位原因为 jquery 引入错误. layu ...

  6. OpenGL学习笔记(二)画三角形

    目录 渲染管线(Graphics Pipeline) 编码实现 顶点数据 顶点缓冲对象(VBO) 顶点着色器 编译着色器 片段着色器 着色器程序 链接顶点属性 顶点数组对象 最终绘制三角形 索引缓冲对 ...

  7. Cobaltstrike与MSF会话派生

    Cobaltstrike与MSF会话派生 前言 一般在渗透的过程中,Get到shell之后一般来说我喜欢上线到Cobaltstrike,但是Cobaltstrike的会话是60S更新一次,有时候功能也 ...

  8. Upload-labs 文件上传靶场通关攻略(上)

    Upload-labs 文件上传靶场通关攻略(上) 文件上传是Web网页中常见的功能之一,通常情况下恶意的文件上传,会形成漏洞. 逻辑是这样的:用户通过上传点上传了恶意文件,通过服务器的校验后保存到指 ...

  9. Redis-缓存穿透/击穿/雪崩

    1. 简介 如图所示,一个正常的请求 客户端请求张铁牛的博客. 服务首先会请求redis,查看请求的内容是否存在. redis将请求结果返回给服务,如果返回的结果有数据则执行7:如果没有数据则会继续往 ...

  10. DrJava试用笔记

    安装方便:只要配好JAVA_HOME,用java -jar drjava-stable-20120818-r5686.jar即可启动,算是绿色软件: 特色功能:交互式命令行,可以在调试程序时改变变量值 ...