• 每日OJ题_DFS回溯剪枝①_力扣46. 全排列(回溯算法简介)


    目录

    回溯算法简介

    力扣46. 全排列

    解析代码


    回溯算法简介

    回溯算法是一种经典的递归算法,通常⽤于解决组合问题、排列问题和搜索问题等。

            回溯算法的基本思想:从一个初始状态开始,按照⼀定的规则向前搜索,当搜索到某个状态无法前进时,回退到前一个状态,再按照其他的规则搜索。回溯算法在搜索过程中维护一个状态树,通过遍历状态树来实现对所有可能解的搜索。

            回溯算法的核心思想:“试错”,即在搜索过程中不断地做出选择,如果选择正确,则继续向前搜索,否则,回退到上一个状态,重新做出选择。回溯算法通常用于解决具有多个解,且每个解都需要搜索才能找到的问题。

            回溯算法的模板:

    1. void backtrack(vector<int>& path, vector<int>& choice, ...)
    2. {
    3. // 满⾜结束条件
    4. if (/* 满⾜结束条件 */)
    5. {
    6. // 将路径添加到结果集中
    7. res.push_back(path);
    8. return;
    9. }
    10. // 遍历所有选择
    11. for (int i = 0; i < choices.size(); i++)
    12. {
    13. // 做出选择
    14. path.push_back(choices[i]);
    15. // 做出当前选择后继续搜索
    16. backtrack(path, choices);
    17. // 撤销选择
    18. path.pop_back();
    19. }
    20. }

            其中, path表示当前已经做出的选择, choices表示当前可以做的选择。在回溯算法中,我们需要做出选择,然后递归地调用回溯函数。如果满足结束条件,则将当前路径添加到结果集中。

            否则,我们需要撤销选择,回到上一个状态,然后继续搜索其他的选择。回溯算法的时间复杂度通常较高,因为它需要遍历所有可能的解。但是,回溯算法的空间复杂度较低,因为它只需要维护一个状态树。在实际应用中,回溯算法通常需要通过剪枝等方法进行优化,以减少搜索的次数,从而提高算法的效率。

            回溯算法是一种非常重要的算法,可以解决许多组合问题、排列问题和搜索问题等。回溯算法的核心思想是搜索状态树,通过遍历状态树来实现对所有可能解的搜索。回溯算法的模板非常简单,但是实现起来需要注意一些细节,比如何做出选择、如何撤销选择等。


    力扣46. 全排列

    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 中的所有整数 互不相同
    1. class Solution {
    2. public:
    3. vectorint>> permute(vector<int>& nums) {
    4. };

    解析代码

    之前练习next_permutation时写过,代码在下面注释起来了。

     

    深搜的思路是循环模仿遍历树的结点,到叶子结点就返回,不是就进入循环。

            在下面for循环里要考虑这个位置要填哪个数。根据题目要求我们肯定不能填已经填过的数,因此很容易想到的一个处理手段就是定义一个标记数组来标记已经填过的数,那么在填这个数的时候我们遍历题目给定的所有数,如果这个数没有被标记过,我们就尝试填入,并将其标记,继续尝试填下一个位置,

            回溯的时候要撤销这一个位置填的数以及标记,并继续尝试其他没被标记过的数。

    1. class Solution {
    2. vectorint>> ret;
    3. vector<int> path;
    4. bool cheak[6] = {0}; // 映射下标->数字是否用过->剪枝
    5. public:
    6. vectorint>> permute(vector<int>& nums) {
    7. dfs(nums);
    8. return ret;
    9. }
    10. void dfs(vector<int> nums)
    11. {
    12. if(nums.size() == path.size())
    13. {
    14. ret.push_back(path);
    15. return;
    16. }
    17. for(int i = 0; i < nums.size(); ++i)
    18. {
    19. if(cheak[i] == false) // 如果没有用过才使用->剪枝
    20. {
    21. path.push_back(nums[i]);
    22. cheak[i] = true;
    23. dfs(nums); // 此时路径已经加上一个了,在让其进入递归
    24. path.pop_back(); // 回溯,恢复现场,(递归往回走了)
    25. cheak[i] = false;
    26. }
    27. }
    28. }
    29. };
    30. // class Solution {
    31. // public:
    32. // vector> permute(vector& nums) {
    33. // vector> vv;
    34. // sort(nums.begin(), nums.end());
    35. // do
    36. // {
    37. // vv.push_back(nums);
    38. // }while(next_permutation(nums.begin(), nums.end()));
    39. // return vv;
    40. // }
    41. // };

  • 相关阅读:
    Billie Eilish的XR音乐会和三星Galaxy发布会现场竟都是他们制作
    React学习之路-目录结构
    JAVA使用Grafana和Loki抓取聚合日志
    域控制器BSP开发工程师面试题
    DVWA - Brute Force
    无涯教程-JavaScript - MROUND函数
    APISpace 成语大全API
    2022年计算机专业毕业设计课题推荐
    Mac如何远程连接Ubuntu主机(一)ssh连接|Mac通过ssh远程连接Ubuntu主机
    基于SSM+Vue的鲜花销售系统设计与实现
  • 原文地址:https://blog.csdn.net/GRrtx/article/details/136239517