回溯法专题

回溯法专题

  回溯法(Backtracking)指的是在每个状态的固化,比如f(1)是一个状态,f(2)是另一个状态。从f(1)到f(2),状态改变,各种依赖状态的数据也改变了,那么从f(2)到f(1)的时候,又回到了f(1)的状态了。回溯常常配合深度优先执行,在往深度时候,数据产生变化,然后在递归回来的时候,又回到了之前的状态。

46. Permutations

  拿这道全排列来讲,它按照正常顺序列出了全部的排列结果,也可以叫做字典序,主要是输入是按从小到大的话,输出就是字典序。

   按照递归的方式来做这道题的话,那么就是for循环的起点和变量,决定了交换哪两个数。

   比如输入是1,2,3,第一次交换就是 1,3,2;在递归栈出来的时候,变成1,2,3;然后再次交换是2,1,3,再是2,3,1,最后再出来的时候就会变成1,2,3。

   主要在这段

            swap(nums,n,j);
            sortNums(nums,n+1,len);
            swap(nums,n,j);
class Solution {
    List<List<Integer>> result = new ArrayList<List<Integer>>();
    public List<List<Integer>> permute(int[] nums) {

        if(nums ==null)
            return result;
        int len = nums.length;
        sortNums(nums, 0, len);
        return result;
    }
    public void sortNums(int[] nums, int n, int len){
        List<Integer> list = new ArrayList();
        if(len-1 ==n){
            for(int i =0; i <len; i++){
                list.add(nums[i]);

            }
            result.add(list);
            return;
        }
        for(int j =n; j <len; j++){
            swap(nums,n,j);
            sortNums(nums,n+1,len);
            swap(nums,n,j);
        }
    }
    public void swap(int[] nums, int n, int j){
        int temp = 0;
        temp = nums[n];
        nums[n] = nums[j];
        nums[j] = temp;
    }
}

47. Permutations II

  在这道题中,它拥有重复元素,在排列的时候使用上一题的swap方式,那么会有两个1,1,2出来,题意是过滤掉这个重复值,可以使用set过滤。

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if(nums == null){
            return  result;
        }else if(nums.length < 2){
            List<Integer> rList = new ArrayList<Integer>();
            for(int v : nums){
                rList.add(v);
            }
            result.add(rList);
            return result;
        }
        Set<Long> filter = new HashSet<Long>();
        perm(nums,0,nums.length-1,result,filter);

        return result;

    }

    void perm(int[] nums,int k,int m,List<List<Integer>> result,Set<Long> filter){
        if(k == m){
            List<Integer> rList = new ArrayList<Integer>();
            long tag = 0;
            for(int v :nums){
                tag = tag * 10 + v;
                rList.add(v);
            }
            if(!filter.contains(tag)){
                result.add(rList);
                filter.add(tag);
            }

        }else {
            for(int i=k;i<=m;i++){
                if(i != k && nums[i] == nums[k])
                    continue;
                swap(nums,i,k);
                perm(nums,k+1,m,result,filter);
                swap(nums,i,k);
            }
        }
    }

    boolean swap(int[] nums,int i,int j){
        if(i == j)
           return false;
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
        return true;
    }

}

79. Word Search

  这个题目乍一看懵逼,仔细看就知道了,一个迷宫,如果能吃掉你输入的序列,那么就是true,这是回溯的经典题目。

   关键点是朝着四个方向尝试,并且使用数组visited标记是否已经访问过,记得再出栈的时候将visited还原。

public class WordSearch {
    public void test(){
        char[][] board ={
                {‘A‘,‘B‘,‘C‘,‘E‘},
                {‘S‘,‘F‘,‘C‘,‘S‘},
                {‘A‘,‘D‘,‘E‘,‘E‘}
        };
        // true
        System.out.println(exist(board,"ABCCED"));
        // true
        System.out.println(exist(board,"SEE"));
        // false
        System.out.println(exist(board,"ABCB"));

        char[][] board1={
                {‘A‘,‘B‘,‘C‘,‘E‘},
                {‘S‘,‘F‘,‘E‘,‘S‘},
                {‘A‘,‘D‘,‘E‘,‘E‘}};
        // true
        System.out.println(exist(board1, "ABCESEEEFS"));

    }

    public boolean exist(char[][] board, String word) {
        boolean[][] visited = new boolean[board.length][board[0].length];
        for(int i=0;i<board.length;i++){
            for(int j=0;j<board[i].length;j++){
                if(dps(board,i,j,0,word,visited)){
                    return true;
                }
            }
        }
        return false;
    }
    boolean dps(char[][] board,int i,int j,int index,String word,boolean[][] visited){
        if(index >= word.length()){
            return true;
        }
        if(i<0 || i>=board.length){
            return false;
        }
        if(j<0 || j>=board[0].length){
            return false;
        }
        if(visited[i][j]){
            return false;
        }
        if(board[i][j] == word.charAt(index)){
            visited[i][j]=true;
            boolean exist = dps(board,i-1,j,index+1,word,visited) || dps(board,i+1,j,index+1,word,visited)
                    || dps(board,i,j+1,index+1,word,visited) || dps(board,i,j-1,index+1,word,visited);
            visited[i][j]=false;
            return exist;
        }else {
            return false;
        }
    }
}

78. Subsets

  集合子集分割,高中数学第一章。

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if(nums == null || nums.length == 0){
            return result;
        }
        recursion(nums,0,new ArrayList<>(),result);
        return result;
    }
    void recursion(int[] nums,int i,List<Integer> each,List<List<Integer>> result){
        if(i>=nums.length){
            result.add(new ArrayList<>(each));
        }else {
            each.add(nums[i]);
            recursion(nums,i+1,each,result);
            each.remove(each.size()-1);
            recursion(nums,i+1,each,result);
        }
    }
}

90. Subsets II

  重复元素的子集分割。

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if(nums == null || nums.length == 0){
            return result;
        }
        Arrays.sort(nums);
        Set<List<Integer>> filter = new HashSet<>();
        recursion(nums,0,new ArrayList<>(),filter);
        result.addAll(filter);
        return result;
    }
    void recursion(int[] nums, int i, List<Integer> each, Set<List<Integer>> result){
        if(i >= nums.length){
            result.add(new ArrayList<>(each));
        }else {
            each.add(nums[i]);
            recursion(nums,i+1,each,result);
            each.remove(each.size()-1);

            recursion(nums,i+1,each,result);
        }
    }
}

原文地址:https://www.cnblogs.com/chentingk/p/11697506.html

时间: 2024-10-01 02:29:10

回溯法专题的相关文章

Leetcode之回溯法专题-39. 组合总数(Combination Sum)

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的数字可以无限制重复被选取. 说明: 所有数字(包括 target)都是正整数. 解集不能包含重复的组合. 示例 1: 输入: candidates = [2,3,6,7], target = 7, 所求解集为: [ [7], [2,2,3] ] 示例 2: 输入: candidates = [2,3,5], target

Leetcode之回溯法专题-17. 电话号码的字母组合(Letter Combinations of a Phone Number)

[Leetcode]17. 电话号码的字母组合(Letter Combinations of a Phone Number) 题目描述: 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合. 给出数字到字母的映射如下(与电话按键相同).注意 1 不对应任何字母. 示例: 输入:"23"输出:["ad", "ae", "af", "bd", "be", "bf&qu

【LeetCode】回溯法 backtracking(共39题)

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } [10]Regular Expression Matching [17]Letter Combinations of a Phone Number [22]Generate Parentheses (2019年2月13日) 给了一个N,生成N对括号的所有情况的字符串. n = 3 [ "((()))", "(()())", "(

五大常用算法之四:回溯法

(转自:http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html) 1.概念 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径. 回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”. 许

回溯法 -数据结构与算法

1.回溯法算法思想: 定义: 回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”. 1.回溯法适用:有许多问题,当需要找出它的解集(全部解)或者要求回答什么解是满足某些约束条件的最优解时,往往要使用回溯法. 2.有组织的穷举式搜索:回溯法的基本做法是搜索或者有的组织穷尽搜索.它能避免搜索所有的可能性.即避免不必要的搜索.这种方

0-1背包-回溯法

算法描述: 0-1背包的回溯法,与装载问题的回溯法十分相似.在搜索解空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树.当右子树中有可能包含最优解时才进入右子树进行搜索.否则将右子树剪去. 计算右子树上界的更好算法是: 将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包. 算法实现: 由Bound函数计算当前节点处的上界. 类Knap的数据成员记录解空间树的节点信息,以减少参数传递及递归调用所需的栈空间. 在解空间树的当前扩展结点处,仅当要进

回溯法浅谈

回溯法是初学者学习暴力法的第一个障碍,所谓回溯就是指当把问题分成若干步骤并递归求解时,如果当前步骤没有合法选择,则函数将返回上一级递归调用,这种现象称为回溯.正是因为这个原因,递归枚举算法常被称为回溯法,应用十分普遍. 八皇后问题 1 int tot=0; 2 int c[maxn]; //c[x]表示x行所在的列数 3 void dfs(int n,int cur){ 4 if(cur==n) ++tot; 5 for(int i=0;i<n;i++){ 6 int flag=0; 7 c[c

回溯法求迷宫问题

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”.本文使用回溯法求解迷宫问题迷宫问题,有一个m行n列的矩阵代表迷宫,1代表此路不通,0代表此路通.指定入口和出口,判断是否能从入口进,从出口走出.此程序只判断从路口到出口是否能走通,找到的路不一定是最短路(最短路的程序在下一篇中使用BFS算法给出),注意:从入口到

经典算法学习之回溯法

回溯法的应用范围:只要能把待求解的问题分成不太多的步骤,每个步骤又只有不太多的选择就可以考虑使用回溯法. 若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束. 而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束. 回溯法将问题的候选解按照某一顺序逐一枚举和检验.当发现当前候选解不可能是解时,就选择下一个候选解,若当前候选解符合要求,且还未达到求解的规模,则继续扩展当前候 选解的规模,如果候选解已经满足了所有要求,并且也达到了问题的规模,那么该候选解就是