首页 > 编程语言 >常用算法 -- 回溯算法

常用算法 -- 回溯算法

时间:2024-08-22 15:22:15浏览次数:12  
标签:return target -- chessboard int 算法 回溯 root

1 简介

        回溯算法是一种基于试错思想的搜索算法,也是一种重要的编程技巧,常用于解决组合、排列、切割等问题。它通过不断尝试解决问题的每一步,一旦发现当前步骤不能得到有效的正确解,就会返回上一步或多步,并尝试其他可能的分步解决方案。这种策略使得回溯算法能够有效地找到问题的所有可行解或最优解。

2 理论基础

  • 试错思想: 回溯算法的核心是试错思想,它尝试每一个可能的解决方案,并在发现当前方案不可行时撤销前一步或几步的操作。
  • 深度优先搜索(dfs): 回溯算法与深度优先搜索紧密相关,通过尽可能深地搜索树的分支来寻找问题的解。
  • 递归实现: 回溯算法通常采用递归方式实现,因为递归调用可以很自然地映射到算法的回溯(即返回上一层)操作上。

3 应用场景

1. 组合问题: 从给定个数的元素中,按照一定的规则选择元素形成组合。

    应用实例:旅行商问题(TSP):这类问题要求找出最短可能路径,使得每个城市只访问一次并返回起点。

2. 排列问题: 找出所有可能的排列方式。

    应用实例: 全排列问题。经典的全排列问题要求对一组数字进行所有可能的排列,例如对于数字集合{1, 2, 3},求出所有六个可能的排列。

3. 数独问题:填写9×9的方格,使得每行、每列和每个宫内的数字均不重复。

    应用实例: 数独求解器。数独游戏要求在9×9的网格中填入数字,每行、每列及9个3×3的子网格中的数字(1-9)均不重复。

4. 决策问题:在多个可选方案中寻找最优解,通常涉及多个约束条件。

    应用实例: 作业调度。例如,在生产线上,需要安排不同作业执行的顺序,以优化生产效率和资源利用率。

5. 棋盘问题: 在N×N的棋盘上放置N个“皇后”,使得它们互不攻击。

    应用实例: 八皇后问题。这是一个经典的回溯算法问题,要求在8×8的棋盘上放置8个皇后,使得它们不会互相攻击。

6. 切割问题:将字符串或数组按照规定的方式进行分割。

    应用实例: 字符串分割。例如,将一个字符串按一定规则分割成多个有效的子串。

7. 递增子序列问题:找出数组或列表中的递增子序列。

    应用实例:最长递增子序列。在一个数字序列中找到最长的递增子序列。

8. 搜索问题:在图或网络中搜索从起点到终点的有效路径。

    应用实例:图的深度优先搜索。例如,在地图导航系统中,搜索从一个地点到另一个地点的所有可能路线。

4 算法流程

  1. 选择:从当前状态的所有可选方案中选择一个进行下一步尝试。
  2. 约束:检查当前状态是否满足给定的约束条件。
  3. 目标:判断当前状态是否是目标状态,即是否找到了一个符合要求的解。
  4. 回溯:当发现当前状态不可行或已经找到一个解时,返回上一步的状态,继续尝试其他可能的选项。

公式就参考carl哥的回溯三部曲

  • 回溯函数模板返回值以及参数
  • 回溯函数终止条件
  • 回溯搜索的遍历过程

框架如下:

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

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

5 实例

5.1 简单题

257. 二叉树的所有路径

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。叶子节点 是指没有子节点的节点。

示例 1:

输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]

示例 2:

输入:root = [1]
输出:["1"]

提示:

  • 树中节点的数目在范围 [1, 100] 内
  • -100 <= Node.val <= 100
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> res = new ArrayList<>();
        bfs(root, "", res);
        return res;
    }

    public void bfs(TreeNode root, String path, List<String> paths){
        if(root == null){
            // 终止节点
            return;
        }

        StringBuilder sb = new StringBuilder(path);
        sb.append(Integer.toString(root.val));

        if(root.left == null && root.right == null){
            // 遍历到叶子结点结束
            paths.add(sb.toString());
        }else{
            // 由于是二叉树,先序遍历即可
            sb.append("->");
            bfs(root.left, sb.toString(), paths);
            bfs(root.right, sb.toString(), paths);
        }
    }
}

5.2 经典中等题

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
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40
class Solution {
    List<Integer> path = new ArrayList<Integer>();
    List<List<Integer>> res = new ArrayList<>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        dfs(candidates, target, 0, 0);
        return res;
    }

    public void dfs(int[] nums, int target, int index, int sum) {
        if (sum == target) {
            res.add(new ArrayList<>(path));
            return;
        }
        // 非结果直接忽略
        if (sum > target){
            return;
        }

        for (int i = index; i < nums.length; i++) {
            // 加入当前值
            path.add(nums[i]);
            sum += nums[i];
            
            dfs(nums, target, i, sum);

            // 回溯
            path.remove(path.size() - 1);
            sum -= nums[i];
        }
    }
}

5.3 困难题

51. N 皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例 1:

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

输入:n = 1
输出:[["Q"]]

提示:

  • 1 <= n <= 9

对于三皇后图解:

三皇后本无解,一步一步递归验证,终可得出结论。如下图:

class Solution {
    List<List<String>> res = new ArrayList<>();

    public List<List<String>> solveNQueens(int n) {
        char[][] chessboard = new char[n][n];
        for (char[] c : chessboard) {
            Arrays.fill(c, '.');
        }
        backTracking(n, 0, chessboard);
        return res;
    }

    public void backTracking(int n, int row, char[][] chessboard) {
        if (row == n) {
            res.add(Array2List(chessboard));
            return;
        }

        for (int col = 0; col < n; ++col) {
            if (isValid(row, col, n, chessboard)) {
                chessboard[row][col] = 'Q';
                backTracking(n, row + 1, chessboard);
                chessboard[row][col] = '.';
            }
        }

    }
       
    // 数组转换
    public List Array2List(char[][] chessboard) {
        List<String> list = new ArrayList<>();

        for (char[] c : chessboard) {
            list.add(String.copyValueOf(c));
        }
        return list;
    }

    // 判断当前节点是否符合规范
    public boolean isValid(int row, int col, int n, char[][] chessboard) {
        // 检查列
        for (int i = 0; i < row; ++i) {
            if (chessboard[i][col] == 'Q') {
                return false;
            }
        }

        // 检查45度对角线
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }

        // 检查135度对角线
        for (int i = row - 1, j = col + 1; i >= 0 && j <= n - 1; i--, j++) {
            if (chessboard[i][j] == 'Q') {
                return false;
            }
        }
        return true;
    }
}

6 回溯算法的优缺点

  1. 优点
    • 能够找到所有可能的解决方案。
    • 易于理解和实现。
    • 适合解决复杂问题。
  2. 缺点
    • 时间复杂度高,尤其是问题规模较大时。
    • 可能会产生大量的重复计算。
    • 需要较多的存储空间来保存中间状态。

7 总结

        总的来说,回溯算法是一种强大的问题解决工具,特别适合处理那些可以通过逐步构建解决方案来解决的问题。然而,由于其高时间复杂度和空间需求,在实际应用中可能需要进行适当的优化。通过剪枝、约束加强、启发式搜索等策略,可以有效提高回溯算法的效率,使其更好地应用于实际问题中。

标签:return,target,--,chessboard,int,算法,回溯,root
From: https://blog.csdn.net/qq_67342067/article/details/141122531

相关文章

  • redis入门
    1介绍1.1简介        Redis(RemoteDictionaryServer)是一个使用ANSIC编写的支持网络、基于内存、分布式、可选持久性的键值对存储数据库。根据月度排行网站DB-Engines.com的数据,Redis是最流行的键值对存储数据库。                      ......
  • 【Linux】挂载硬盘并设置开机自动挂载
    @目录1.什么是挂载2.文件管理器点击挂载3.手动挂载查看可挂载的硬盘扇区在想要的位置创建一个目录作为挂载点4.设置开机自动挂载本文介绍了在Linux系统下挂载硬盘的概念和步骤,并讲解了开机自动挂载的方法。1.什么是挂载秉承着Linux“一切皆文件”的理念,硬盘这种东西在系统中以......
  • KMP-笔记
    tip:以下内容仅本人理解,如有问题,欢迎指出前言(?首先我们要知道KMP是干嘛的KMP是一个字符串匹配算法,相当于AC自动机的弱化版,如果你完全理解了KMP和Trie树的话,那你也离学会AC自动机不远了对于字符串匹配,我们有一个字符串和一个模式串,需要求字符串的子串里有没有这个模式串。......
  • C#通过反射拿到枚举,但是里面有个“value__”,怎么办?
    1usingSystem;2usingUnityEngine;3usingSystem.Reflection;4publicenumExampleEnum5{6Value1,7Value28}9classProgram:MonoBehaviour10{11voidStart()12{13TypeenumType=typeof(ExampleEnum);14......
  • CentOS 7.4 Linux 下文件名乱码快速解决方案
    原文链接: https://blog.csdn.net/qingyujin/article/details/119026866文件是在WIndows下创建的,Windows的文件名中文编码默认为GBK,而Linux中默认文件名编码为UTF8,由于编码不一致所以导致了文件名乱码的问题,解决这个问题需要对文件名进行转码。文件名转码工具convmv没安装......
  • 定期备份kingbase数据库
    原文链接:https://blog.csdn.net/weixin_47387140/article/details/1285845371.书写备份数据脚本/bin/bash#date:0106#managed_by:mzhbakdir=/var/lib/kingbaseif[!-d$bakdir];thenmkdir-p$bakdirfiprocessing(){clearfor((i=0;$i<=100;i+=5))doecho-e"\e[6;9H......
  • 高性能无锁队列 Disruptor 核心原理分析及其在i主题业务中的应用
    小结:生产者生产数据时,需要入队。消费者消费数据时,需要出队。入队时,不能覆盖没有消费的元素。出队时,不能读取没有写入的元素。因此,Disruptor中需要维护一个入队索引(生产者数据生产到哪里,对应AbstractSequencer中的cursor)和一个出队索引(所有消费者中消费进度最小的序号)。 ......
  • 02-HTML&JS相关练习
    1、使用html写一个网页,要求满足以下条件:(1)网页中含有任意一张图片,图片路径使用绝对路径(这里绝对路径无法识别故使用相对路径),鼠标悬停在图片时出现“马哥教育”文本,且点击图片可跳转至马哥教育官方页面(2)网页中包含账号、密码登录,且账号提前定义好是admin且不可更改,输入密码时......
  • NSSCTF [HNCTF 2022 Week1]Interesting_include
    <?php//WEB手要懂得搜索//flagin./flag.phpif(isset($_GET['filter'])){$file=$_GET['filter'];if(!preg_match("/flag/i",$file)){die("error");}include($file);}else{highlight_file(__......
  • 基于 FastAPI+Vue.js 构建的食谱管理平台
    1.2自托管的食谱管理平台:Mealie主语言:Python,Star:6.1k,周增长:400该项目是基于FastAPI+Vue.js构建的食谱管理平台。它提供了简洁友好的界面,用户可以在线编辑和管理食谱,并通过简单的操作从多种来源(URL)导入食谱内容,支持膳食计划、购物清单、多语言、API集成和Docker部署等功能......