回溯法可以看成是蛮力法的升级版,它从解决问题每一步的所有可能选项里系统地选择可行的解决方案。回溯算法基于深度优先搜索算法和递归算法,在搜索过程中寻找问题的解。
回溯算法建议先画图,整理出思路。先明白回溯的结束条件,进入回溯的条件,回溯的选择,回溯的参数选择。
LeetCode46 全排列
画图分析:每个叶子节点的形成路径就是一个全排列。更详细原文解析
代码实现
class Solution {
public List<List<Integer>> permute(int[] nums) {
int lens = nums.length;
List<List<Integer>> result = new ArrayList<>();// 定义一个数组保存结果集
// 特殊情况
if(lens == 0) return result;
// 使用 Deque 是 Java 官方 Stack 类的建议
Deque<Integer> tmp = new ArrayDeque<>(lens);
boolean[] visited = new boolean[lens];
backtrack(result, nums, tmp, visited, lens);
return result;
}
private void backtrack(List<List<Integer>> res, int[] nums, Deque<Integer> tmp, boolean[] visited, int lens) {
// 结束条件,将全排列加入到结果集中
if(tmp.size() == lens){
res.add(new ArrayList<>(tmp));
return;
}
for (int i = 0; i < lens; i++) {
if(visited[i] == true) continue;
visited[i] = true;
tmp.addLast(nums[i]);
backtrack(res, nums, tmp, visited, lens);
// 状态重置
visited[i] = false;
tmp.removeLast();
}
}
}
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
int len = nums.length;
if(len == 0) return res; // 判空
Arrays.sort(nums); // 排序,升序
boolean[] used = new boolean[len];
Deque<Integer> tmp = new ArrayDeque<>(len);
backtrack(nums, len, 0, used, tmp, res);
return res;
}
public void backtrack(len, int depth, boolean[] used, Deque<Integer> tmp, List<List<Integer>> res){
// 结束条件
if(depth == len){
res.add(new ArrayList<>(tmp));
return;
}
for(int i = 0; i <len; i++){
if(!used[i]){
// 剪枝,去除重复
if(i>0 && nums[i-1] == nums[i] && !used[i-1]){
continue;
}
used[i] = true;
tmp.addLast(nums[i]);
traceback(nums, len, depth+1, used, tmp, res);
// 状态重置
tmp.removeLast();
used[i] = false;
}
}
}
}
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
int len = candidates.length;
if (len == 0) return res;
// 按照升序排序
Arrays.sort(candidates);
backtrack(target, 0, new ArrayDeque<>(), candidates, len, res);
return res;
}
private void backtrack(int remainder, int start, Deque<Integer> tmp, int[] candidates, int len, List<List<Integer>> res){
// 结束条件,当减的余数刚好为0的时候满足解
if(remainder == 0){
res.add(new ArrayList<>(tmp));
return;
}
for(int i = start; i < len; i++){
if(remainder - candidates[i]>=0){
tmp.add(candidates[i]);
backtrack(remainder-candidates[i], i, tmp, candidates, len, res);
// 状态重置
tmp.removeLast();
}
}
}
}
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
int len = candidates.length;
if(len == 0) return res;
Arrays.sort(candidates);
backtrack(candidates, target, 0, len, res,new ArrayDeque<>(len));
return res;
}
private void backtrack(int[] candidates, int remainder, int start, int len, List<List<Integer>> res,ArrayDeque<Integer> tmp){
if(remainder == 0){
res.add(new ArrayList<>(tmp));
return;
}
for(int i = start; i < len; i++){
//大剪枝,余数为负数,直接跳过剩余
if(remainder - candidates[i]<0) break;
//小剪枝,去除重复
if(i>start && candidates[i]==candidates[i-1]) continue;
tmp.add(candidates[i]);
backtrack(candidates, remainder-candidates[i], i+1, len, res, tmp);
tmp.removeLast();
}
}
}
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
int len = nums.length;
if(len == 0) return res;
backtrack(nums,0, res, len, new ArrayList<>());
return res;
}
private void backtrack(int[] nums, int depth, List<List<Integer>> res, int len, ArrayList<Integer> tmp ){
res.add(new ArrayList<>(tmp));
for(int i = depth; i < len; i++){
tmp.add(nums[i]);
backtrack(nums, i+1, res, len, tmp);
tmp.remove(tmp.size()-1);
}
}
}
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
int len = nums.length;
if(len == 0) return res;
Arrays.sort(nums);
backtrack(nums, 0, res, len, new ArrayList<>());
return res;
}
private void backtrack(int[] nums, int start, List<List<Integer>> res, int len, ArrayList<Integer> tmp){
res.add(new ArrayList<>(tmp));
for(int i = start; i < len; i++){
if(i>start && nums[i] == nums[i-1]) continue;
tmp.add(nums[i]);
backtrack(nums, i+1, res, len, tmp);
tmp.remove(tmp.size()-1);
}
}
}
public class Solution {
public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
{
// 特殊情况
if(matrix == null || rows < 1 || cols < 1 || str == null) return false;
boolean[] used = new boolean[rows*cols]; // 定义布尔值矩阵,标记每个格子是否被访问过
int pathLength = 0; // 路径字符串中的下标
// 遍历矩阵中的每个格子,找到路径中的第一个字符并进入回溯
for(int row = 0; row < rows; row++){
for(int col = 0; col < cols; col++){
if(backtrack(matrix, rows, cols, row, col, str, pathLength, used)){
return true;
}
}
}
return false;
}
// 判断路径是否经过(row,col)格子
private boolean backtrack(char[] matrix, int rows, int cols, int row, int col, char[] str, int pathLength, boolean[] used){
boolean hasPath = false; // 标志是否有路径
int index = row*cols + col; // 在矩阵中的下标
// 找到路径的结束条件
if(pathLength == str.length){
return true;
}
if(row>=0 && col>=0 && row<rows && col<cols && matrix[index]==str[pathLength] && !used[index]){
pathLength++;
used[index] = true;
hasPath = backtrack(matrix, rows, cols, row-1, col, str, pathLength, used)
|| backtrack(matrix, rows, cols, row+1, col, str, pathLength, used)
|| backtrack(matrix, rows, cols, row, col-1, str, pathLength, used)
|| backtrack(matrix, rows, cols, row, col+1, str, pathLength, used);
// 在没有找到的情况下,状态重置
if(!hasPath){
pathLength--;
used[index] = false;
}
}
return hasPath;
}
}
public class Solution {
public int movingCount(int threshold, int rows, int cols)
{
// 特殊情况
if(threshold < 0 || rows < 0 || cols < 0) return 0;
boolean[] used = new boolean[rows * cols];
int count = backtrack(threshold, rows, cols, 0, 0, used);
return count;
}
private int backtrack(int threshold, int rows, int cols, int row, int col, boolean[] used){
int index = row * cols + col;
// 结束条件
if(row < 0 || col < 0 || row >= rows || col >= cols || used[index] || cal(row) + cal(col) > threshold){
return 0;
}
used[index] = true;
return 1 + backtrack(threshold, rows, cols, row - 1, col, used)
+ backtrack(threshold, rows, cols, row + 1, col, used)
+ backtrack(threshold, rows, cols, row, col - 1, used)
+ backtrack(threshold, rows, cols, row, col + 1, used);
}
// 计算一个整数的各个位数之和
private int cal(int num){
int sum = 0;
while(num>0){
sum += num % 10;
num /= 10;
}
return sum;
}
}