【代码随想录】回溯算法专栏(java版本)

前言

总结回溯算法的题型
按照规模将其题型都熟练掌握

学习的链接大框架为:
回溯算法理论基础

  • LinkedList<Integer> sonlist=new LinkedList<>();
    双向链表可以删除最前或者添加最前的节点,删除的时候通过removeLast();
  • List<Integer> sonlist=new ArrayList<>();
    单链表只能添加,或者删除,删除的时候通过remove(下标值)

组合

77. 组合(中等)

题目:

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

示例 2:

输入:n = 1, k = 1
输出:[[1]]

提示:

1 <= n <= 20
1 <= k <= n


思路:

class Solution {
    
    List<List<Integer>> list =new ArrayList<List<Integer>>();

    LinkedList<Integer> sonlist=new LinkedList<>();

    public List<List<Integer>> combine(int n, int k) {
    	if (k <= 0 || n < k) {
            return list;
        }   
        backtrace(n,k,1);
        return list;

    }

    public void backtrace(int n,int k,int startindex){
        if(sonlist.size()==k){
            list.add(new ArrayList<>(sonlist));
            return;
        }

        for(int i=startindex;i<=n;i++){
            sonlist.addLast(i);
            backtrace(n,k,i+1);
            //不用带下标
            sonlist.removeLast();
        }
    }
}

也可以将以上的双向列表换为如下:Deque<Integer> sonlist=new ArrayDeque<>();
进一步的优化,将其for条件的循环更改为:

for(int i=startindex;i<=n-(k-sonlist.size())+1;i++){

这是因为如果 n = 7, k = 4,从 5 开始搜索就已经没有意义了

优化过程:
已经选择的元素个数:sonlist.size();

还需要的元素个数为: k - sonlist.size();

在集合n中至多要从该起始位置 : n - (k - sonlist.size()) + 1,开始遍历

为什么有个+1呢,因为包括起始位置,我们要是一个左闭的集合。

代码如下:

class Solution {

    List<List<Integer>> list = new ArrayList<List<Integer>>();
    LinkedList<Integer> sonlist = new LinkedList<>();

    public List<List<Integer>> combine(int n, int k) {
        if(k <= 0 || n < k){
            return list;
        }
        backtrace(n,k,1);
        return list;

    }
    public void backtrace(int n,int k,int startindex){
        if(sonlist.size() == k){
            list.add(new ArrayList<>(sonlist));
            return ;
        }

        for(int i = startindex;i <= (n - (k - sonlist.size())) + 1;i++){
            sonlist.addLast(i);
            backtrace(n,k,i+1);

            // 移除最后一个元素不用带下标
            sonlist.removeLast();
        }
    }
}

如果内部使用LinkedList函数则需要注意的点在删除最后一个元素的值

class Solution {
    //列表的形式
    List<List<Integer>> list =new ArrayList<List<Integer>>();
    //双向列表的形式
    List<Integer> sonlist=new ArrayList<>();

    public List<List<Integer>> combine(int n, int k) {   
        backtrace(n,k,1);
        return list;

    }

    public void backtrace(int n,int k,int startindex){
        if(sonlist.size()==k){
            //双向列表强转换为列表
            list.add(new ArrayList(sonlist));
            return;
        }

        for(int i=startindex;i<=n;i++){
            
            sonlist.add(i);

            backtrace(n,k,i+1);
            
            sonlist.remove(sonlist.size()-1);

        }
    }
}

216. 组合总和 III(中等)

题目:

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:

只使用数字1到9
每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

示例 1:

输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。

示例 2:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解释:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
没有其他符合的组合了。
示例 3:

输入: k = 4, n = 1
输出: []
解释: 不存在有效的组合。
在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。

提示:

2 <= k <= 9
1 <= n <= 60


思路:

计算其sum值,可以将其sum值也遍历,不用额外定义一个int sum在函数外。
注意其终止条件

class Solution {
    List<List<Integer>> list=new ArrayList<>();
    LinkedList<Integer> sonlist =new LinkedList<>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        backtrace(k,n,1,0);
        return list;

    }
    public void backtrace(int k,int n,int startindex,int sum){
        if(sum>n)return;
        
        if(sonlist.size()==k){
            if(sum==n)list.add(new ArrayList<>(sonlist));
            return;
        }
        for(int i=startindex;i<=9-(k-sonlist.size())+1;i++){
            sonlist.addLast(i);
            sum=sum+i;
            backtrace(k,n,i+1,sum);
            sonlist.removeLast();
            sum=sum-i;
        }
    }
}

39. 组合总和(中等)

题目:

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

提示:

1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都 互不相同
1 <= target <= 500


思路:

这题的思路主要跟上面的区别,在于数组是可以重复使用。

class Solution {
    List<List<Integer>> list=new ArrayList<List<Integer>>();
    //删除回溯使用双端队列比较好
    LinkedList<Integer> sonlist=new LinkedList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates); // 先进行排序
        backtrace(candidates,target,0,0);
        return list;
    }

    public void backtrace(int []candidates,int target,int sum,int startindex){
        //如果sum大于target 优先返回
        if(sum>target)return ;

        //记得转换类型
        if(sum==target){
            list.add(new ArrayList<>(sonlist));
            return;
        }
        
        //从startindex开始遍历,而且条件是小于数组长
        for(int i=startindex;i<candidates.length;i++){

            sonlist.addLast(candidates[i]);
            sum=sum+candidates[i];
            //这里的数组值是可以重复的,所以startindex是可以为i本身自已。
            backtrace(candidates,target,sum,i);

            //移除最新的节点,也要移除sum中那个数组值
            sonlist.removeLast();
            sum=sum-candidates[i];
        }
    }
}

40. 组合总和 II(中等)

题目:

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

注意:解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

提示:

1 <= candidates.length <= 100
1 <= candidates[i] <= 50
1 <= target <= 30


思路:

这道题跟上面的思路是大同小异,差不多,区别在于不可重复使用,但是数组中有值一样,遍历出来的列表,可能会有重复的,类似这种.
在这里插入图片描述
解决上面的问题:把所有组合求出来,再用set或者map去重,这么做很容易超时!
所以要在搜索的过程中就去掉重复组合。

可以通过外加这一行代码,具体如下所示:
只要大于下标值,而且值和之前那个相等,既跳过该下标即可

if(i>startindex&&candidates[i]==candidates[i-1])continue;

完整代码如下所示:

class Solution {
    List<List<Integer>> list =new ArrayList<List<Integer>>();
    LinkedList<Integer> sonlist=new LinkedList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtrace(candidates,target,0,0);
        return list;

    }
    public void backtrace(int []candidates,int target,int sum,int startindex){
        if(sum>target)return;

        if(sum==target){
            list.add(new ArrayList<>(sonlist));
            return ;
        }

        for(int i=startindex;i<candidates.length;i++){
            if(i>startindex&&candidates[i]==candidates[i-1])continue;
            sonlist.addLast(candidates[i]);
            sum=sum+candidates[i];
            backtrace(candidates,target,sum,i+1);
            
            sonlist.removeLast();
            sum=sum-candidates[i];
        }

    }
}

也可以通过一维的标记数组,标记其已经使用过该数字了

class Solution {
    List<List<Integer>> list =new ArrayList<List<Integer>>();
    LinkedList<Integer> sonlist=new LinkedList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        //定义一个一维标记数组
        boolean [] flag=new boolean [candidates.length];
        backtrace(candidates,target,0,0,flag);
        return list;

    }
    public void backtrace(int []candidates,int target,int sum,int startindex,boolean [] flag){
        if(sum>target)return;

        if(sum==target){
            list.add(new ArrayList<>(sonlist));
            return ;
        }

        for(int i=startindex;i<candidates.length;i++){
            //比较的是前一个数组,数组的boolean也是上一个值
            if(i>0&&candidates[i]==candidates[i-1]&&flag[i-1]==false)continue;
            sonlist.addLast(candidates[i]);
            sum=sum+candidates[i];
            //一开始初始化为true
            flag[i]=true;
            backtrace(candidates,target,sum,i+1,flag);
            //回溯之后变为false,标记数组
            flag[i]=false;
            sonlist.removeLast();
            sum=sum-candidates[i];
        }

    }
}

可配合如下进行理解:
(关于该图来源于 代码随想录的网址)
在这里插入图片描述

子集

78. 子集(中等)

题目:

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

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

示例 1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums 中的所有元素 互不相同


思路:

class Solution {

    List<List<Integer>> list = new ArrayList<List<Integer>>();
    // 此处使用ArrayList 以及 LinkedList 用法不同
    LinkedList<Integer> sonlist = new LinkedList<>();

    public List<List<Integer>> subsets(int[] nums) {
        backtrace(nums,0);
        return list;

    }
    public void backtrace(int[] nums,int index){
        //「遍历这个树的时候,把所有节点都记录下来,就是要求的子集集合」。
        list.add(new ArrayList<>(sonlist));
        //终止条件本身可不加
        if(sonlist.size() > nums.length)return ;

        for(int i = index;i < nums.length;i++){
            sonlist.addLast(nums[i]);
            backtrace(nums,i + 1);
            sonlist.removeLast();
        }
    }
}

或者使用ArryaList

class Solution {

    List<List<Integer>> list = new ArrayList<List<Integer>>();
    // 此处使用ArrayList 以及 LinkedList 用法不同
    List<Integer> sonlist = new ArrayList<>();

    public List<List<Integer>> subsets(int[] nums) {
        backtrace(nums,0);
        return list;

    }
    public void backtrace(int[] nums,int index){
        //「遍历这个树的时候,把所有节点都记录下来,就是要求的子集集合」。
        list.add(new ArrayList<>(sonlist));
        //终止条件本身可不加
        if(sonlist.size() > nums.length)return ;

        for(int i = index;i < nums.length;i++){
            sonlist.add(nums[i]);
            backtrace(nums,i + 1);
            sonlist.remove(sonlist.size() - 1);
        }
    }
}

90. 子集 II(中等)

题目:

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

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

示例 1:

输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

1 <= nums.length <= 10
-10 <= nums[i] <= 10


思路:
添加标记数组:

class Solution {
    List<List<Integer>> list = new ArrayList<List<Integer>>();
    LinkedList<Integer> sonlist = new LinkedList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {

        //先排好顺序,因为数组内的数值可能重复,子集不能重复
        Arrays.sort(nums);

        boolean[] flag = new boolean[nums.length];
        backtrace(nums,0,flag);
        return list;
    }
    public void backtrace(int[] nums,int startindex,boolean[] flag){
        //该算法为找子集,每一次遍历都可以输出添加
        list.add(new ArrayList<>(sonlist));

        if(sonlist.size() > nums.length)return;

        for(int i = startindex;i < nums.length;i++){
            // 核心代码在这一块细节
            //通过大于下标以及如果两者相等,跳过该循环
            if(i > 0 && nums[i] == nums[i - 1] && flag[i - 1] == false)continue;

            sonlist.addLast(nums[i]);
            flag[i]=true;
            backtrace(nums,i + 1,flag);
            flag[i]=false;
            sonlist.removeLast();
        }
    }
}

不添加标记数组的方式:

class Solution {
    List<List<Integer>> list = new ArrayList<List<Integer>>();
    LinkedList<Integer> sonlist = new LinkedList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {

        //先排好顺序,因为数组内的数值可能重复,子集不能重复
        Arrays.sort(nums);

        backtrace(nums,0);
        return list;
    }
    public void backtrace(int[] nums,int startindex){
        //该算法为找子集,每一次遍历都可以输出添加
        list.add(new ArrayList<>(sonlist));

        if(sonlist.size() > nums.length)return;

        for(int i = startindex;i < nums.length;i++){
            // 核心代码在这一块细节
            //通过大于下标以及如果两者相等,跳过该循环,此处不是大于 0 
            if(i > startindex && nums[i] == nums[i - 1])continue;

            sonlist.addLast(nums[i]);
            backtrace(nums,i + 1);
            sonlist.removeLast();
        }
    }
}

排列

46. 全排列(中等)(不包含重复数字)

题目:

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例 3:

输入:nums = [1]
输出:[[1]]

提示:

1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同


思路:

这里和77.组合问题 、131.切割问题和78.子集问题最大的不同就是for循环里不用startIndex了

因为排列问题,每次都要从头开始搜索,例如元素1在[1,2]中已经使用过了,但是在[2,1]中还要再使用一次1。

排列问题总结
每层节点都是从0开始搜索而不是startindex
需要使用uded数组记录path放了哪些元素

在这里插入图片描述

class Solution {
    List<List<Integer>> list = new ArrayList<List<Integer>>();
    LinkedList<Integer> sonlist = new LinkedList<>();
    public List<List<Integer>> permute(int[] nums) {
        boolean[] flag = new boolean[nums.length];
        backtrace(nums,flag);
        return list;
    }
    public void backtrace(int[] nums,boolean[] flag){
        // 对应全排列,只有全部满了,才可使用list添加
        if(sonlist.size() == nums.length){
            list.add(new ArrayList<>(sonlist));
        }

        // 此处不需要用index,也不需要提前排序
        //每一层回溯遍历,都是从0开始,判断是否为true
        // 1为true,在回溯的时候,1还是为true,则跳过1,来到了2。遍历完1 2 3类似这样子后。
        for(int i = 0;i < nums.length;i++){
            //如果这一层用了true的节点,则跳回
            if(flag[i] == true)continue;

            sonlist.addLast(nums[i]);
            flag[i] = true;

            backtrace(nums,flag);
            flag[i] = false;
            sonlist.removeLast();
        }
    }
}

47. 全排列 II(中等)

题目:

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

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]

示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

提示:

1 <= nums.length <= 8
-10 <= nums[i] <= 10


思路:

class Solution {
    List<List<Integer>> list = new ArrayList<List<Integer>>();
    LinkedList<Integer> sonlist = new LinkedList<>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        boolean[] flag = new boolean[nums.length];
        Arrays.sort(nums);
        backtrace(nums,flag);
        return list;
    }
    public void backtrace(int[] nums,boolean[] flag){
        // 对应全排列,只有全部满了,才可使用list添加
        if(sonlist.size() == nums.length){
            list.add(new ArrayList<>(sonlist));
        }

        // 此处不需要用index,也不需要提前排序
        //每一层回溯遍历,都是从0开始,判断是否为true
        // 1为true,在回溯的时候,1还是为true,则跳过1,来到了2。遍历完1 2 3类似这样子后。
        for(int i = 0;i < nums.length;i++){
            if(i > 0 && nums[i] == nums[i-1] && flag[i-1] == false)continue;

            //如果这一层用了true的节点,则跳回
            if(flag[i] == true)continue;

            sonlist.addLast(nums[i]);
            flag[i] = true;

            backtrace(nums,flag);
            flag[i] = false;
            sonlist.removeLast();
        }
    }
}

补充
查重的时候一定要排序以及去重:

  • 排序:Arrays.sort(nums);
  • 去重:
// 以下两者都可
if(i > 0 && nums[i] == nums[i-1] && flag[i-1] == false)continue;
if(i > 0 && nums[i] == nums[i-1] && flag[i-1] == true)continue;

其他

491. 递增子序列(中等)

题目:

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

示例 1:

输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

示例 2:

输入:nums = [4,4,3,2,1]
输出:[[4,4]]

提示:

1 <= nums.length <= 15
-100 <= nums[i] <= 100


思路:

区别于上面其他思路:
因为原数组中不像之前的都是排序后在进行查重,所以不可使用之前的方法
在搜索遍历的时候就进行查重 而且 确定好排序

在这里插入图片描述
而且不可用上面思路中的标记数组
在这里插入图片描述
需要使用类似哈希表中的数组计数

注意题目中说了,数值范围[-100,100],所以完全可以用数组来做哈希

class Solution {
    List<List<Integer>> list =new ArrayList<List<Integer>>();
    List<Integer> sonlist=new ArrayList<>();
    boolean [] flag;
    public List<List<Integer>> findSubsequences(int[] nums) {
        flag=new boolean [nums.length];
        backtrace(nums,0);
        return list;
    }
    public void backtrace(int [] nums,int startindex){
        //确定列表的尺寸大于等于2才添加进去
        if(sonlist.size()>=2){
            list.add(new ArrayList<>(sonlist));
        }
        
        if(startindex>nums.length)return ;

        int[] used = new int[201];
        
        for(int i=startindex;i<nums.length;i++){
            //因为原数组中不像之前的都是排序后在进行查重,所以不可使用之前的方法
            //在搜索遍历的时候就进行查重 而且 确定好排序 
            if(!sonlist.isEmpty()&&nums[i]<sonlist.get(sonlist.size()-1) ||used[nums[i] + 100] == 1 )continue;
            used[nums[i] + 100] = 1;
            flag[i]=true;
            sonlist.add(nums[i]);
            backtrace(nums,i+1);

            sonlist.remove(sonlist.size() - 1);
        }
    }
}

51. N 皇后(困难)*

题目:51. N 皇后

行、列、左斜、右斜

class Solution {
    public List<List<String>> solveNQueens(int n) {

        List<List<String>> solutions = new ArrayList<List<String>>();

        int[] queens = new int[n];
        // 全部初始化为-1,此处不初始化都可,因为后面会把其覆盖
        Arrays.fill(queens,-1);

        // 区分皇后主要通过三个set集合,对应列,左斜,右斜。行的话,本身回溯就已经判断了
        Set<Integer> column = new HashSet<>();
        Set<Integer> left = new HashSet<>();
        Set<Integer> right = new HashSet<>();
        
        backtrace(solutions,queens,n,0,column,left,right);
        return solutions;

    }
    // 传入整个list集合 queens的数组,对应矩阵n,以及row 行,之后就是各个集合
    public void backtrace(List<List<String>> solutions,int[] queens,int n,int row,Set<Integer> column,Set<Integer> left,Set<Integer> right){
        // 放置的皇后个数已经等于n了,将其拼接在一起
        if(row == n){
            List<String> list = new ArrayList<>();
            // 具体拼接通过另外一个函数
            list = generate(queens,n);
            solutions.add(list);
        }else {
            // 遍历每一皇后的存储位置
            for(int i = 0;i < n;i++){
                // 一一对应判断是否有其值
                if(column.contains(i)){
                    continue;
                }

                // 左斜是通过row - i,规律
                int sonleft = row - i;
                if(left.contains(sonleft)){
                    continue;
                }

                // 右斜是通过row + i,规律
                int sonright = row + i;
                if(right.contains(sonright)){
                    continue;
                }

                // 对应回溯,一开始赋值为i,主要是标记,后面拼接给予Q
                queens[row] = i;
                column.add(i);
                left.add(sonleft);
                right.add(sonright);
                backtrace(solutions,queens,n,row + 1,column,left,right);

                // 回溯结束赋值为原来的值
                queens[row] = -1;
                column.remove(i);
                left.remove(sonleft);
                right.remove(sonright);
            }
            
        }
    }

    public List<String> generate(int[] queens,int n){
        List<String> list = new ArrayList<>();
        for(int i = 0;i < n;i++){
            // 每一行的  一个个拼接 覆盖
            char[] row = new char[n];
            // 赋值初始值
            Arrays.fill(row,'.');
            row[queens[i]] = 'Q';

            // 最后list add的时候要通过new String构建对象
            list.add(new String(row));            
        }
        return list;
    }
}

类似这道题的思路:
查看如下:52. N皇后 II(困难)

class Solution {
    public int totalNQueens(int n) {
        Set<Integer> column = new HashSet<>();
        Set<Integer> left = new HashSet<>();
        Set<Integer> right = new HashSet<>();
 
        return backtrace(n,0,column,left,right);
    }

    public int backtrace(int n,int row,Set<Integer> column,Set<Integer> left,Set<Integer> right){
        // 放置的皇后个数已经等于n了,直接返回1即可
        if(row == n){
            return 1;
        }else {
            // 个数统计 count
            int count = 0;
            for(int i = 0;i < n;i++){
                if(column.contains(i)){
                    continue;
                }

                int sonleft = row - i;
                if(left.contains(sonleft)){
                    continue;
                }

                int sonright = row + i;
                if(right.contains(sonright)){
                    continue;
                }

                column.add(i);
                left.add(sonleft);
                right.add(sonright);
                count += backtrace(n,row + 1,column,left,right);
                column.remove(i);
                left.remove(sonleft);
                right.remove(sonright);
            }
            // 回溯提前结束,直接返回count的个数
            return count;
        }
        
    }

}

37. 解数独(困难)

题目:leetcode:37. 解数独

类似的题目如下,只不过下面这个题不是回溯,类似模拟算法
题目:leetcode:36. 有效的数独(中等)

class Solution {
    public boolean isValidSudoku(char[][] board) {
        // 每个数组大致都定义为 9行 或者 9列,最后的9为index 表示该元素+1
        int[][] row = new int [9][9];
        int[][] column = new int[9][9];
        // box 3*3的表框,后面的9 为index 表示该元素+1
        int[][][] box = new int[3][3][9];

        for(int i = 0;i < 9;i++){
            for(int j = 0;j < 9;j++){
                // 取出该元素
                char c = board[i][j];
                if(c != '.'){
                    // 注意此处要多减去1,因为下标与位置相差1
                    int index = c - '0' - 1;
                    row[i][index]++;
                    column[j][index]++;
                    box[i / 3][j / 3][index]++;
                    if(row[i][index] > 1 || column[j][index] > 1 || box[i / 3][j / 3][index] > 1){
                        return false;
                    }
                }
            }
        }
        return true;
    }
}
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农研究僧

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值