回溯算法小结---LeetCode和剑指offer回溯例题Java实现

回溯法可以看成是蛮力法的升级版,它从解决问题每一步的所有可能选项里系统地选择可行的解决方案。回溯算法基于深度优先搜索算法和递归算法,在搜索过程中寻找问题的解。

回溯算法建议先画图,整理出思路。先明白回溯的结束条件,进入回溯的条件,回溯的选择,回溯的参数选择。

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();
        }
    }
}

LeetCode47 全排列2

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;
            }
        }
    } 
}

LeetCode39 组合总和

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();
            }
        }
    }
}

LeetCode40 组合总和2

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();
            
        }
    }
}

LeetCode78 子集

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);
        }
    }
}

LeetCode90 子集2

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);
        }
    }
}

剑指offer面试题12:矩阵中的路径

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;
    }
}

剑指offer面试题13:矩阵中的路径

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;
    }
}

发布了56 篇原创文章 · 获赞 8 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Dawn510/article/details/104305368