力扣爆刷第175天之回溯全家桶(组合、排列、子集、分割、棋盘)

力扣爆刷第175天之回溯全家桶

零、回溯题心法:

心法一共分两步,第一步熟背心法,第二步学会分辨题目类型。
回溯一共就三类,分为组合、子集、排列。其中组合与子集又是一个问题,只不过子集还会搜集非叶子节点,但本质都是组合。
这三类又有其他变体,变体主要就是约束条件,共有三类:
①、元素无重,不可重复选。
②、元素可重,不可重复选。(需要横向去重,i > index && nums[i] == nums[i-1],前提是把数组进行了排序。)
③、元素无重,可重复选。
具体展开来讲分为:
组合、分割、子集、排列、棋盘、其他

组合问题:

元素可以重复使用index无需加1,元素不可以重复使用需要从下一个位置开始index+1。

如果组合数是固定个数的可以剪枝,for循环的次数小于n了就停止。

对于数组中有重复数字,但是要求结果集不能重复,需要树层去重。

分割问题:注意操作字符串的边界问题。

子集问题:

如果收集所有节点,即叶子结点和非叶子节点。

求递增子序列,如3,7,6,7 。由于不能排序树层去重,就无法使用used数组或者i > index && nums[i]== nums[i-1],因为相同的数并不挨着。又得解决树层中相同的数不能重复使用,就可以在树层间使用set数组,每递归进入下一层就是一个新数组。

求全排列,由于排列讲究顺序,如1,2,3排列结果会有2,1,3。这样就不能指定for循环的开始位置为当前数的下一个,而是固定从0开始,但是需要使用一个数组记录下来哪些使用过滤哪些没有使用,要纵向记录,因为纵向不能重复使用数字,递归标记,回溯后就没有标记,只影响纵向收集。

对于有重复数字的全排列,纵向横向都要去重,for从0开始,避免纵向重复使用used数组记录,为true为使用过,为false为没使用过,即used[i]=true跳过,横向去重需要数组排序,当nums[i]=nums[i-1]时,used[i-1]=false是回溯完的,需要树层去重。

模板:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

一、77. 组合

题目链接:https://leetcode.cn/problems/combinations/description/
思路:本题是最基础的回溯题,从类型上来说是组合题。具体为元素无重,不可复选。求组合数,从N个数中选择K个数,既然是组合,需要使用index指定下一次递归的索引位置,另外规定结果集单个元素的长度,需要剪枝。

class Solution {
    List<List<Integer>> resList = new ArrayList<>();
    List<Integer> list = new ArrayList<>();
    public List<List<Integer>> combine(int n, int k) {
        backTracking(n, 1, k);
        return resList;
    }
    
    void backTracking(int n, int index, int k) {
        if(list.size() == k) {
            resList.add(new ArrayList(list));
            return;
        }
        for(int i = index; i <= n && k - list.size() <= n - i + 1; i++) {
            list.add(i);
            backTracking(n, i+1, k);
            list.remove(list.size()-1);
        }
    }
}

二、216. 组合总和 III

题目链接:https://leetcode.cn/problems/combination-sum-iii/
思路:N个数求到达目标和的组合。题目类型为元素无重,不可复选。常规回溯模板,额外记录sum和,提前早停,正常指定index索引。

class Solution {
    List<List<Integer>> resList = new ArrayList<>();
    List<Integer> list = new ArrayList<>();
    int sum = 0;
    public List<List<Integer>> combinationSum3(int k, int n) {
        backTracking(k, n, 1);
        return resList;
    }

    void backTracking(int k, int n, int index) {
        if(list.size() == k) {
            if(sum == n) resList.add(new ArrayList(list));
            return;
        }
        for(int i = index; i <= 9 && sum + i <= n && k - list.size() <= 9 - i + 1; i++) {
            sum += i;
            list.add(i);
            backTracking(k, n, i+1);
            sum -= i;
            list.remove(list.size()-1);
        }
    }
    
}

三、17. 电话号码的字母组合

题目链接:https://leetcode.cn/problems/letter-combinations-of-a-phone-number/
思路:本题来说也是组合题,只不过原数据集是变动的,具体类别是,元素无重,不可复用,但是原数据集是可变的,所以解题思路直接使用组合模板,每次纵向递归都更换一个新的数据集即可。

class Solution {
    String[] source = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    List<String> list = new ArrayList<>();
    StringBuilder sb = new StringBuilder();
    public List<String> letterCombinations(String digits) {
        if(digits.length() == 0) return list;
        backTracking(digits, 0);
        return list;
    }
    
    void backTracking(String digits, int level) {
        if(sb.length() == digits.length()) {
            list.add(sb.toString());
            return;
        }
        String temp = source[digits.charAt(level) - '0'];
        for(int i = 0; i < temp.length(); i++) {
            sb.append(temp.charAt(i));
            backTracking(digits, level+1);
            sb.deleteCharAt(sb.length()-1);
        }
    }
}

四、39. 组合总和

题目链接:https://leetcode.cn/problems/combination-sum/description/
思路:本题是求目标和的组合种类,具体类型为,元素无重,可复选,针对这种类型来说,还是常规套模板,但是可复选,需要让索引不加1,这样纵向递归就可以重复使用同一个值,就可以达到可复选的目的,同时既然是求目标和的,就可以剪枝,直接排序数组,就可以通过早停进行剪枝。

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

五、40. 组合总和 II

题目链接:https://leetcode.cn/problems/combination-sum-ii/
思路:本题还是求组合数的各种类别,还是组合,但是类型变了,具体为元素有重,不可复选,需要横向去重。即集合元素是有重复值的,但是每个元素只能用一次,所以,必须要考虑去重的问题。先说模板怎么使用,再说在模板上如何修改。首先数组需要排序,让相同的元素挨着,然后模版是正常模板,index索引+1,然后是横向去重,但是纵向不需要。如1,1,1,1。目标和是2,递归返回重新选择结果集就会重复,所以在递归返回后,如果当前值和前一个值是相等的,并且前一个值是递归返回的值,就要跳过。
总结:元素有重、不可复选,需要横向树层去重。

class Solution {
    List<List<Integer>> resList = new ArrayList<>();
    List<Integer> list = new ArrayList<>();
    int sum = 0;
    boolean[] flag;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        flag = new boolean[candidates.length];
        backTracking(candidates, target, 0);
        return resList;
    }

    void backTracking(int[] candidates, int target, int index) {
        if(sum == target) {
            resList.add(new ArrayList(list));
            return;
        }
        for(int i = index; i < candidates.length && sum + candidates[i] <= target; i++) {
            if(i > 0 && !flag[i-1] && candidates[i] == candidates[i-1]) continue;
            flag[i] = true;
            sum += candidates[i];
            list.add(candidates[i]);
            backTracking(candidates, target, i + 1);
            flag[i] = false;
            sum -= candidates[i];
            list.remove(list.size()-1);
        }
    }
    
}

六、131. 分割回文串

题目链接:https://leetcode.cn/problems/palindrome-partitioning/description/
思路:本题叫分割回文串,其实就是分割,既然是分割就需要index索引位置,所以完全可以按照组合来做,每次判断的片段其实是从index到i的,所以只需要使用正常的回溯模板,指定index,然后再单独写一个对字符串是否是回文的判断即可。

class Solution {
    List<List<String>> resList = new ArrayList<>();
    List<String> list = new ArrayList<>();
    public List<List<String>> partition(String s) {
        backTracking(s, 0);
        return resList;
    }

    void backTracking(String s, int index) {
        if(index == s.length()) {
            resList.add(new ArrayList(list));
            return;
        }
        for(int i = index; i < s.length(); i++) {
            if(isTrue(s, index, i)) {
                list.add(s.substring(index, i+1));
                backTracking(s, i + 1);
                list.remove(list.size()-1);
            }
        }
    }

    boolean isTrue(String s, int left, int right) {
        while(left < right) {
            if(s.charAt(left) != s.charAt(right)) return false;
            left++;
            right--;
        }
        return true;
    }
}

七、93. 复原 IP 地址

题目链接:https://leetcode.cn/problems/restore-ip-addresses/description/
思路:复原IP地址也是有意思的题目,要求把字符串进行一个划分,分割成符合IP规则的字符串。这里提一下,分割的本质其实就是组合,因为分割要求的是不改变原本的元素之间的顺序,排序的话会改变,组合的话不会改变,所以,只要遇到分割题目,直接套用组合的模板就行,然后指定index索引,其次就是单独写函数对片段进行判断,判断片段是否符合规则,如果符合规则,就可以使用,最后就是考虑剪枝,早停的操作,基本就差不多了。

输入:s = “25525511135”
输出:[“255.255.11.135”,“255.255.111.35”]

class Solution {
    List<String> list = new ArrayList<>();
    List<String> res = new ArrayList<>();
    public List<String> restoreIpAddresses(String s) {
        if(s.length() > 12) return list;
        backTracking(s, 0);
        return res;
    }
    
    void backTracking(String s, int index) {
        if(list.size() == 4) {
            if(index == s.length()) {
                res.add(String.join(".", list));
            }
            return;
        }
        for(int i = index; i < s.length() && i - index < 3; i++) {
            String t = isTrue(s, index, i);
            if(t == null) continue;
            list.add(t);
            backTracking(s, i + 1);
            list.remove(list.size()-1);
        }
    }
    
    String isTrue(String s, int left, int right) {
        if(left != right && s.charAt(left) == '0') return null;
        String t = s.substring(left, right + 1);
        if(Integer.parseInt(t) > 255) return null;
        return t;
    }
    
}

八、78. 子集

题目链接:https://leetcode.cn/problems/subsets/description/
思路:前面分别谈论了组合问题、分割问题,其实这两类问题还有现在的子集问题都组合问题,共同的特点都是对结果并没有顺序性的要求,即和排序是相对的,并不需要排序。那我们来讲一下子集的特点,子集本质就是组合,只不过不光在叶子节点进行收集,也会根据要求在非叶子节点进行收集。具体的类别,也是和之前提到的类似,本题的类别是:元素无重、不可复用。
故对于元素无重、不可复用的类别,直接套用组合模板即可,唯一要注意的点就是需要在所有的节点进行收集,不光是叶子节点,非叶子节点也需要进行收集。

class Solution {
    List<List<Integer>> resList = new ArrayList<>();
    List<Integer> list = new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
        backTracking(nums, 0);
        return resList;
    }
    
    void backTracking(int[] nums, int index) {
        resList.add(new ArrayList(list));
        for(int i = index; i < nums.length; i++) {
            list.add(nums[i]);
            backTracking(nums, i + 1);
            list.remove(list.size() - 1);
        }
    }
}

九、90. 子集 II

题目链接:https://leetcode.cn/problems/subsets-ii/
思路:上一题的子集题,是最基础的类型,为元素无重、不可复选,本题在上一题的基础上进行了改进,难度更大了一点,具体的类别为:元素有重、不可复选。模板还是套用组合的模板,但是需要考虑去重的问题,举一个例子,如下面展示的那样,原集合的元素虽然有重复,但是我收集出来的子集却没有重复,从递归的角度考虑,元素有重、不可复选,只影响横向,并不影响纵向。如1,2,2就是一个例子,纵向递归到重复元素正常收集,但是如果是当前节点收集了1,向下递归收集了2,为1,2.后面递归返回后for向右走,右收集了2,就会出现收集的子集中有两个1,2。所以问题的根源就是重复元素会在递归返回后的横向遍历的过程中影响唯一性。所以我们需要进行横向的去重。
横向的去重方式有很多种,下面展示了两种,一般使用第二种,这样可以省去一个flag数组。
即元素有重、不可复选,需要横向去重,具体方法为,先对Nums数组排序,然后通过 i > index && nums[i] == nums[i-1]进行判断,满足这个条件的就要被跳过去。而 i > index 这个条件是省去flag数组的关键,当i > index才进行后续的判定,是不会影响相同元素的纵向递归的,如1,2,2就不会拦截,就可以正常向下走。也就起到了替换flag数组的作用。
输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

横向去重方式一:

class Solution {
    List<List<Integer>> resList = new ArrayList<>();
    List<Integer> list = new ArrayList<>();
    boolean[] flag;
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        flag = new boolean[nums.length];
        backTracking(nums, 0);
        return resList;
    }

    void backTracking(int[] nums, int index) {
        resList.add(new ArrayList(list));
        for(int i = index; i < nums.length; i++) {
            if(i > 0 && nums[i] == nums[i-1] && !flag[i-1]) continue;
            flag[i] = true;
            list.add(nums[i]);
            backTracking(nums, i + 1);
            flag[i] = false;
            list.remove(list.size() - 1); 
        }
    }
}

横向去重方式二:

class Solution {
    List<List<Integer>> resList = new ArrayList<>();
    List<Integer> list = new ArrayList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        backTracking(nums, 0);
        return resList;
    }

    void backTracking(int[] nums, int index) {
        resList.add(new ArrayList(list));
        for(int i = index; i < nums.length; i++) {
            if(i > index && nums[i] == nums[i-1]) continue;
            list.add(nums[i]);
            backTracking(nums, i + 1);
            list.remove(list.size() - 1); 
        }
    }
}

十、491. 非递减子序列

题目链接:https://leetcode.cn/problems/non-decreasing-subsequences/description/
思路:本题也是子集问题,只不过也是在上一题的基础又做了改进,类型依然是元素有重、不可复选,但是不是简单的子集了,加了一个附加要求,子集必须得是非递减的子序列,而且长度要大于2。大于等于2好说,在收集子集的时候,list长度只要大于等于2就可以收集,那么对于子集是非递减的子序列,那么就需要通过原数组的排序,来完成横向的去重。那么横向去重就要另想他法,其实很简答,只需要在每次向下一层递归之前维护一个set,这样每次递归到下一层都是新的set,并不影响纵向递归,横向的话,是同一个set,正好可以完成横向的元素去重。此时就可以完成横向去重,完成横向去重以后,剩下的就是比对list中收集的元素与当前遍历的元素之间的大小关系。

综上,来一个小总结:对于元素有重、不可复选的类型,是需要横向去重的,去重措施基于场景,对于Nums数组可以排序的,可以使用i > index && nums[i] == nums[i-1]进行去重,对于不可排序的,只能使用set进行去重。

class Solution {
    List<List<Integer>> resList = new ArrayList<>();
    List<Integer> list = new ArrayList<>();
    public List<List<Integer>> findSubsequences(int[] nums) {
        backTracking(nums, 0);
        return resList;
    }

    void backTracking(int[] nums, int index) {
        if(list.size() > 1) {
            resList.add(new ArrayList(list));
        }
        HashSet<Integer> set = new HashSet<>();
        for(int i = index; i < nums.length; i++) {
            if((!list.isEmpty() && list.get(list.size()-1) > nums[i]) || set.contains(nums[i])) continue;
            set.add(nums[i]);
            list.add(nums[i]);
            backTracking(nums, i + 1);
            list.remove(list.size() - 1);
        }
    }
}

十一、46. 全排列

题目链接:https://leetcode.cn/problems/permutations/description/
思路:从本题开始正式进入排列类型,上面的组合、分割、子集都是组合。本题是最基础的全排列,类型为:元素无重、不可复选。
知道类别以后,套用回溯模板即可,另外需要明确的就是,排列不需要指定index索引,因为排序需要顺序颠倒,所以需这一层遍历后面的,下一层是前面的元素。所以,明确好处理排列问题的基本手段。
1、不需要指定index索引位置。
2、需要纵向去重,使用flag数组。这个问题是由1引发的,因为不指定起始位置就可能会出现重复递归同一个元素的场景。

class Solution {
    List<List<Integer>> resList = new ArrayList<>();
    List<Integer> list = new ArrayList<>();
    boolean[] flag;
    public List<List<Integer>> permute(int[] nums) {
        flag = new boolean[nums.length];
        backTracking(nums);
        return resList;
    }
    
    void backTracking(int[] nums) {
        if(list.size() == nums.length) {
            resList.add(new ArrayList(list));
            return;
        }
        for(int i = 0; i < nums.length; i++) {
            if(flag[i]) continue;
            flag[i] = true;
            list.add(nums[i]);
            backTracking(nums);
            flag[i] = false;
            list.remove(list.size()-1);
        }
    }
    
}

十二、47. 全排列 II

题目链接:https://leetcode.cn/problems/permutations-ii/description/
思路:本题是针对排列的变形,具体类别为:元素有重,不可复选。
解决思路:
1、排列无需指定index索引位置。
2、纵向去重,需要使用flag数组。
3、元素有重,需要横向去重,数组需要排序,然后使用i > 0 && nums[i] == nums[i-1] && !flag[i]进行横向去重。

class Solution {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> list = new ArrayList<>();
    boolean[] flag;
    public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        flag = new boolean[nums.length];
        backTracking(nums);
        return res;
    }
    
    void backTracking(int[] nums) {
        if(list.size() == nums.length) {
            res.add(new ArrayList(list));
            return;
        }
        for(int i = 0; i < nums.length; i++) {
            if(flag[i] || (i > 0 && nums[i] == nums[i-1]) && !flag[i-1]) continue;
            flag[i] = true;
            list.add(nums[i]);
            backTracking(nums);
            flag[i] = false;
            list.remove(list.size()-1);
        }
    }
}

十三、332. 重新安排行程

题目链接:https://leetcode.cn/problems/reconstruct-itinerary/description/
思路:有向图固定起点,全遍历,遍历时每个from节点对应的多个to节点应选取字典序最小的。另外题目模拟的是航线,自然节点与节点之间必须得有连线,像出度为0的节点自然是最后才能被遍历的。具体解法,我们可以先构建邻接表,然后模拟后序遍历的思路,只要节点不空就可以向下递归访问,直到抵达出度为0的节点,后序位置收集节点。
最后把收集的集合翻转就是正确的遍历顺序。

class Solution {
    List<String> list = new ArrayList<>();
    Map<String, PriorityQueue<String>> map = new HashMap<>();
    public List<String> findItinerary(List<List<String>> tickets) {
        for(List<String> temp : tickets) {
            if(!map.containsKey(temp.get(0))) map.put(temp.get(0), new PriorityQueue());
            map.get(temp.get(0)).add(temp.get(1));
        }
        dfs("JFK");
        Collections.reverse(list);
        return list;
    }
    
    void dfs(String from) {
        while(map.containsKey(from) && map.get(from).size() > 0) {
            dfs(map.get(from).poll());
        }
        list.add(from);
    }
}

十四、51. N 皇后

题目链接:https://leetcode.cn/problems/n-queens/description/
思路:N皇后本身也不难,就是单个皇后横向、纵向、45度,135度都不能有皇后。本质上还是排序问题,只需要采用正常的元素无重、不可复选的排序模板来写就是,关键点在于某个索引位置是否可以进行皇后的设置。每次向下递归层数加1。
在这里插入图片描述

class Solution {
    List<List<String>> resList = new ArrayList<>();
    char[][] source;
    public List<List<String>> solveNQueens(int n) {
        source = new char[n][n];
        for(char[] cc : source) {
            Arrays.fill(cc, '.');
        }
        backTracking(n, 0);
        return resList;
    }

    void backTracking(int n, int row) {
        if(row == n) {
            List<String> list = new ArrayList<>();
            for(char[] cList : source) {
                list.add(new String(cList));
            }
            resList.add(list);
            return;
        }
        for(int col = 0; col < n; col++) {
            if(isTrue(row, col, n)) {
                source[row][col] = 'Q';
                backTracking(n, row+1);
                source[row][col] = '.';
            }
        }
    }
    boolean isTrue(int x, int y, int n) {
        for(int i = x; i >= 0; i--) {
            if(source[i][y] == 'Q') return false;
        }
        for(int i = x, j = y; i >= 0 && j >= 0; i--, j--) {
            if(source[i][j] == 'Q') return false;
        }
        for(int i = x, j = y; i >= 0 && j < n; i--, j++) {
            if(source[i][j] == 'Q') return false;
        }
        return true;
    }
}

十五、37. 解数独

题目链接:https://leetcode.cn/problems/sudoku-solver/description/
思路:解数独也是经典题目,也是排列类型的题目,属于元素无重、不可复选的类型,可以直接套排列模板,然后针对每一个位置可以选择的数进行单独编写函数进行验证。当然,如果某一个位置,从1-9都试过了不行,说明之前设置数设置错了,需要递归返回,修改之前的值。
在这里插入图片描述

class Solution {
    public void solveSudoku(char[][] board) {
        backTracking(board);
    }

    boolean backTracking(char[][] board) {
        for(int i = 0; i < 9; i++) {
            for(int j = 0; j < 9; j++) {
                if(board[i][j] != '.') continue;
                for(char v = '1'; v <= '9'; v++) {
                    if(isTrue(board, i, j, v)) {
                        board[i][j] = v;
                        if(backTracking(board)) return true;
                        board[i][j] = '.';
                    }
                }
                return false;
            }
        }
        return true;
    }

    boolean isTrue(char[][] board, int x, int y, char v) {
        for(int i = 0; i < 9; i++) {
            if(board[x][i] == v || board[i][y] == v) return false;
        }
        int a = (x / 3) * 3, b = (y / 3) * 3;
        for(int i = a; i < a + 3; i++) {
            for(int j = b; j < b + 3; j++) {
                if(board[i][j] == v) return false;
            }
        }
        return true;
    }
}
力扣是一个在线编程平台,提供了大量的算法题目,可以帮助程序员提高算法能力。回溯算法是一种搜索算法,它通过不断地尝试所有可能的解来求解问题。在回溯算法中,我们首先定义一个解空间,然后从解空间中搜索所有可能的解,直到找到符合要求的解为止。回溯算法通常用于求解组合问题、排列问题、子集问题等。 在 Java 中实现回溯算法,通常需要定义一个递归函数来搜索解空间。在递归函数中,我们首先判断当前状态是否符合要求,如果符合要求,则将当前状态加入到解集中;否则,我们继续搜索下一个状态。在搜索下一个状态时,我们需要对当前状态进行一些修改,然后递归调用自身来搜索下一个状态。当搜索完所有可能的状态后,我们需要回溯到上一个状态,继续搜索其他可能的状态。 以下是回溯算法的一般步骤: 1. 定义解空间:确定问题的解空间,并定义一个数据结构来表示解空间中的每个状态。 2. 确定约束条件:确定哪些状态是合法的,并定义一个函数来判断当前状态是否符合要求。 3. 确定搜索策略:确定搜索解空间的顺序,并定义一个函数来生成下一个状态。 4. 搜索解空间:使用递归函数搜索解空间,如果当前状态符合要求,则将其加入到解集中;否则,继续搜索下一个状态。 5. 回溯:当搜索完所有可能的状态后,回溯到上一个状态,继续搜索其他可能的状态。 以下是一个力扣题目的回溯算法 Java 实现示例: ``` class Solution { List<List<Integer>> res = new ArrayList<>(); List<Integer> path = new ArrayList<>(); public List<List<Integer>> subsets(int[] nums) { dfs(nums, 0); return res; } private void dfs(int[] nums, int start) { res.add(new ArrayList<>(path)); for (int i = start; i < nums.length; i++) { path.add(nums[i]); dfs(nums, i + 1); path.remove(path.size() - 1); } } } ``` 该算法用于求解给定数组的所有子集。在递归函数中,我们首先将当前状态加入到解集中,然后从当前位置开始搜索下一个状态。在搜索下一个状态时,我们将当前元素加入到路径中,并递归调用自身来搜索下一个状态。当搜索完所有可能的状态后,我们需要回溯到上一个状态,继续搜索其他可能的状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

当年拼却醉颜红

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

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

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

打赏作者

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

抵扣说明:

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

余额充值