回溯算法中常见的使用方法逻辑整理

这篇具有很好参考价值的文章主要介绍了回溯算法中常见的使用方法逻辑整理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

回溯算法 常见的使用方法逻辑整理


1. 回溯算法 特点

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

2. 回溯算法 通用方法

用回溯算法解决问题的一般步骤:

  • 针对所给问题,定义问题的解空间,它至少包含问题的一个(最优)解。
  • 确定易于搜索的解空间结构,使得能用回溯法方便地搜索整个解空间 。
  • 以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。

回溯是经过修改的深度优先查找方法,过程包括:对一个状态空间树进行深度优先查找,检查每个节点是否满足条件。如果不满足就回溯到该节点的父节点。算法框架(伪代码)如下:

result = []
backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

3. 常见面试题

一些总结

3.1 单词搜索

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例 1:
回溯算法中常见的使用方法逻辑整理,Python,Go,Java,算法,数据结构

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true

示例 2:
回溯算法中常见的使用方法逻辑整理,Python,Go,Java,算法,数据结构

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “SEE”
输出:true

示例 3:
回溯算法中常见的使用方法逻辑整理,Python,Go,Java,算法,数据结构

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCB”
输出:false

解法

  • 递归参数: 当前元素在矩阵 board 中的行列索引 i 和 j ,当前目标字符在 word 中的索引 k 。

  • 终止条件:

    • 返回 false : (1) 行或列索引越界 或 (2) 当前矩阵元素与目标字符不同 或 (3) 当前矩阵元素已访问过 ( (3) 可合并至 (2) ) 。
    • 返回 true : k = len(word) - 1 ,即字符串 word 已全部匹配。
  • 递推工作:

    • 标记当前矩阵元素: 将 board[i][j] 修改为 空字符 ‘’ ,代表此元素已访问过,防止之后搜索时重复访问。
    • 搜索下一单元格: 朝当前元素的 上、下、左、右 四个方向开启下层递归,使用 或 连接 (代表只需找到一条可行路径就直接返回,不再做后续 DFS ),并记录结果至 res 。
    • 还原当前矩阵元素: 将 board[i][j] 元素还原至初始值,即 word[k] 。
  • 返回值: 返回布尔量 res ,代表是否搜索到目标字符串。

回溯算法中常见的使用方法逻辑整理,Python,Go,Java,算法,数据结构

代码示例

class Solution {
    public boolean exist(char[][] board, String word) {
        char[] words = word.toCharArray();
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board[0].length; j++) {
                if (dfs(board, words, i, j, 0)) return true;
            }
        }
        return false;
    }
    boolean dfs(char[][] board, char[] word, int i, int j, int k) {
        if (i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) return false;
        if (k == word.length - 1) return true;
        board[i][j] = '\0';
        boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) || 
                      dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
        board[i][j] = word[k];
        return res;
    }
}

3.2 分割回文串

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

示例 1:

输入:s = “aab”
输出:[[“a”,“a”,“b”],[“aa”,“b”]]

示例 2:

输入:s = “a”
输出:[[“a”]]

解法

由于需要求出字符串 sss 的所有分割方案,因此我们考虑使用搜索 + 回溯的方法枚举所有可能的分割方法并进行判断。

假设我们当前搜索到字符串的第 iii 个字符,且 s[0…i−1] 位置的所有字符已经被分割成若干个回文串,并且分割结果被放入了答案数组 ans 中,那么我们就需要枚举下一个回文串的右边界 jjj,使得 s[i…j] 是一个回文串。

因此,我们可以从 iii 开始,从小到大依次枚举 jjj。对于当前枚举的 jjj 值,我们使用双指针的方法判断 s[i…j] 是否为回文串:如果 s[i…j]是回文串,那么就将其加入答案数组 ans 中,并以 j+1j+1j+1 作为新的 i 进行下一层搜索,并在未来的回溯时将 s[i…j] 从 ans 中移除。

如果我们已经搜索完了字符串的最后一个字符,那么就找到了一种满足要求的分割方法。

代码示例

class Solution {
    boolean[][] f;
    List<List<String>> ret = new ArrayList<List<String>>();
    List<String> ans = new ArrayList<String>();
    int n;

    public List<List<String>> partition(String s) {
        n = s.length();
        f = new boolean[n][n];
        for (int i = 0; i < n; ++i) {
            Arrays.fill(f[i], true);
        }

        for (int i = n - 1; i >= 0; --i) {
            for (int j = i + 1; j < n; ++j) {
                f[i][j] = (s.charAt(i) == s.charAt(j)) && f[i + 1][j - 1];
            }
        }

        dfs(s, 0);
        return ret;

    }

    public void dfs(String s, int i) {
        if (i == n) {
            ret.add(new ArrayList<String>(ans));
            return;
        }
        for (int j = i; j < n; ++j) {
            if (f[i][j]) {
                ans.add(s.substring(i, j + 1));
                dfs(s, j + 1);
                ans.remove(ans.size() - 1);
            }
        }
    }
}       

3.3 全排列

给定一个不含重复数字的数组 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]]

代码示例

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        List<Integer> input = new ArrayList<>();
        for (int i: nums) input.add(i);
        List<Integer> tmp = new ArrayList<>();
        dfs(input, result, tmp, nums.length);
        return result;
    }

    public void dfs(List<Integer> input, List<List<Integer>> result, List<Integer> temp, int k){
        if(temp.size()==k) {
            result.add(temp);
            return;
        }
        for(int i=0;i<input.size();i++){
            List<Integer> copyInput=new ArrayList<>(input);
            List<Integer> copyTemp=new ArrayList<>(temp);
            copyTemp.add(input.get(i));
            copyInput.remove(i);
            dfs(copyInput,result,copyTemp,k);
        }
    }
}    

3.4 子集

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

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

示例 1:

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

示例 2:

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

解法思路

整体上跟跳跃游戏一致,只是稍作变通

代码示例

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        dfs(0, nums, res,new ArrayList<Integer>());
        return res;
    }

    public void dfs(int i, int[] nums, List<List<Integer>> res,  ArrayList<Integer> tmp) {
        res.add(new ArrayList<>(tmp));
        for(int j = i; j < nums.length;j++) {
            tmp.add(nums[j]);
            dfs(j + 1, nums, res, tmp);
            tmp.remove(tmp.size() -1);
        }
    }
}

3.5 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

回溯算法中常见的使用方法逻辑整理,Python,Go,Java,算法,数据结构

示例 1:

输入:digits = “23”
输出:[“ad”,“ae”,“af”,“bd”,“be”,“bf”,“cd”,“ce”,“cf”]
示例 2:

输入:digits = “”
输出:[]
示例 3:

输入:digits = “2”
输出:[“a”,“b”,“c”]

代码示例

class Solution {
    public List<String> letterCombinations(String digits) {
        // 定义相关数字和字母的mapping关系
        Map<String, String[]> map = new HashMap<String, String[]>(){{
            put("2", new String[]{"a","b","c"});
            put("3", new String[]{"d","e","f"});
            put("4", new String[]{"g","h","i"});
            put("5", new String[]{"j","k","l"});
            put("6", new String[]{"m","n","o"});
            put("7", new String[]{"p","q","r","s"});
            put("8", new String[]{"t","u","v"});
            put("9", new String[]{"w","x","y","z"});
        }};
        // 逃出条件1:什么都没有输入,返回空
        if(digits.length() == 0) {
            return new ArrayList<>();
        }
        // 逃出条件2: 只输入1个数字,返回对应的mapping字母列表
        if(digits.length() == 1) {
            List<String> arrayList = new ArrayList<>(map.get(digits).length);
            Collections.addAll(arrayList, map.get(digits));
            return arrayList;
        }
        String[] strList = digits.split("");
        // 构造递归条件,将完整的输入拆分成2部分
        String[] first = map.get(strList[0]);
        String rest = "";
        for(int i = 1;i < strList.length;i++) {
            rest += strList[i];
        }
        
        List<String> result = new ArrayList<>();
        // 分别进行拼凑,进行递归所有的数字
        for(int i=0;i<first.length;i++) {
            List<String> tmp = letterCombinations(rest);
            for(int j = 0;j<tmp.size();j++) {
                result.add(first[i] + tmp.get(j));
            }
        }
        return result;
    }
}

3.6 组合总和

给你一个 无重复元素 的整数数组 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
输出: []

代码示例

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(candidates);
        dfs(candidates, target, res, 0, new ArrayList<Integer>());
        return res;
    }

    public void dfs(int[] candidates, int target, List<List<Integer>> res, int i ,List<Integer> tmp) {
        if(target < 0) return;
        if(target == 0) {
            res.add(new ArrayList<>(tmp));
            return;
        }   

        for(int start = i; start < candidates.length;start++) {
            tmp.add(candidates[start]);
            dfs(candidates, target - candidates[start], res,start, tmp);
            tmp.remove(tmp.size() - 1);
        }
    }
}

3.7 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入:n = 3
输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”]

示例 2:

输入:n = 1
输出:[“()”]

代码示例

class Solution(object):
    def generateParenthesis(self, n):
        """
        :type n: int
        :rtype: List[str]
        """
        """
        :type n: int
        :rtype: List[str]
        """
        global result
        result = []
        bracket_str = ""
        self.find_bracket(n, n, bracket_str)
        print(result)
        return result

    def find_bracket(self, left, right, bracket_str):
        """
        """
        global result
        if left == 0 and right == 0:
            result.append(bracket_str)
        if left > right:
            return

        if left > 0:
            self.find_bracket(left - 1, right, bracket_str + '(')
        if right > 0:
            self.find_bracket(left, right - 1, bracket_str + ')')

4. 参考文档

暂无,相关面试题请参考leetcode以及相关说明文章来源地址https://www.toymoban.com/news/detail-852099.html

到了这里,关于回溯算法中常见的使用方法逻辑整理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • lambda常见使用方法

    前言 之前在携程实习,遇到了lambda表达式,最近逛b站,刚好看到了。顺手整理一下。参考链接 介绍 lambda是一种提高生产力的一种书写代码的方式。 代码中有很多的箭头,箭头左边是参数。例如,遍历的时候,每一个item,然后右边是要进行的操作。 代码中用到了很多的“

    2024年02月05日
    浏览(27)
  • wpscan常见的使用方法

    目录 简单介绍 暴力破解 信息收集 指定用户爆破 命令集合 简单介绍 Wordpress 是一个以PHP和MySQL为平台的免费自由开源的博客软件和内容管理系统。 WPScan 是Kali Linux默认自带的一款漏洞扫描工具,它采用Ruby编写,能够扫描WordPress网站中的多种安全漏洞,其中包括WordPress本身的

    2024年02月03日
    浏览(25)
  • Git基础教程-常用命令整理:学会Git使用方法和错误解决

    目录 一、了解Git的基本概念 二、Git的安装和配置 Git的安装 Git的配置 用户信息 文本编辑器 差异分析工具 查看配置信息 三、Git的基本操作 基本原理 基本操作命令 基本操作示例 场景一:创建新仓库 场景二:拉取并编辑远程仓库 四、常见问题及解决方法 解决冲突 git add文件

    2024年02月10日
    浏览(41)
  • Calender常见方法使用

    ✨前言✨ 本片文章,主要在于了解Calendar类,及对它常用方法的运用 🍒欢迎点赞 👍 收藏 ⭐留言评论 📝私信必回哟😁 🍒博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言 @ 目录 🍊 Calendar类 🍊 Calendar类常用方法 🍊 Calendar类对象字段类型 🍊 Calendar类常

    2024年02月05日
    浏览(32)
  • 高云USB下载器仿真器用户手册(包括在线逻辑分析仪的使用方法)

    仿真器用于高云 GOWIN 公司所生产的 FPGA,可用于程序下载和调试。主要特点如下: 1.支持宽电压1.2V - 3.6V; 2.速度最高可达30Mb/s,极速完成下载和波形调试功能; 3.完美支持在线逻辑分析仪; 4.具有过流保护、TVS 保护,使用更可靠; 5.配高速柔性 USB 线,使用效果佳; 1.无需

    2024年02月09日
    浏览(38)
  • 常见的算法技巧——回溯算法

    回溯算法(Backtracking)是一种常用的算法技巧,用于解决组合优化问题或排列问题。回溯算法通过穷举搜索的方式,尝试所有可能的解,并在搜索过程中进行剪枝,以避免无效的搜索路径。剪枝(Pruning)是一种通过提前排除不必要的分支来减少搜索空间的技巧。 确定解空间

    2024年02月13日
    浏览(27)
  • 【十年网络安全工程师整理】—100渗透测试工具使用方法介绍

     渗透测试是指渗透人员在不同的位置(比如从内网、从外网等位置)利用各种手段对 某个特定网络进行测试,以期发现和挖掘系统中存在的漏洞,然后输出渗透测试报告, 并提交给网络所有者。网络所有者根据渗透人员提供的渗透测试报告, 可以清晰知晓系统中存在的安

    2024年02月02日
    浏览(41)
  • 使用MATLAB生成FPGA调用的coe和mif文件的方法整理

    使用MATLAB生成FPGA调用的coe和mif文件的方法整理 在FPGA设计中,常需要使用初始化文件(coe或mif)来初始化内部存储器或配置寄存器。MATLAB提供了方便的工具和函数来生成这些初始化文件。本文将介绍如何使用MATLAB生成coe和mif文件,并提供相应的源代码示例。 生成coe文件 coe文件是

    2024年04月09日
    浏览(33)
  • selenium常见等待机制及其特点和使用方法

    目录 1、强制等待  2、隐式等待  3、显示等待  强制等待是在程序中直接调用Thread.sleep(timeout) ,来完成的, 该用法的优点是使用起来方便,语法也比较简单,缺点就是需要强制等待固定的时间,可能会造成测试的时间过长。 引入等待的原因是很多时候,程序运行的速度是大

    2024年02月14日
    浏览(42)
  • opencv常见类cv::rect使用方法

    下面是一篇关于C++中的cv::Rect相关的博文,包括对其的介绍、C++代码示例以及一些应用场景。希望对您有所帮助。 使用cv::Rect进行矩形区域操作 在计算机视觉领域中,经常需要对图像中的矩形区域进行操作和处理。OpenCV库提供了一个非常方便的类cv::Rect,用于表示和操作矩形

    2024年02月09日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包