• 22.括号生成


     22.括号生成

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

    示例 1:

    输入:n = 3
    输出:["((()))","(()())","(())()","()(())","()()()"]
    示例 2:

    输入:n = 1
    输出:["()"]
     

    思路:回溯算法最佳实践:括号生成 :: labuladong的算法小抄 (gitee.io)

    合法括号组合满足的性质:

    1.右括号数等于右括号数 ;

    2.对于一个合法的括号组合 p,必然对于任何 0 <= i < len(p) 都有:子串 p[0..i] 中左括号的数量都大于或等于右括号的数量(因为是从左往右读的)

    回溯思路:

    1.回溯算法的过程中要穷举所有组合(满足题目条件的组合/不满足题目条件的组合);

    2.从所有组合中排除不满足条件的组合,最后得到满足条件的组合。

    具体而言:

    1.在此位置先放左括号,进行backTrack递归,就相当于以左括号为根节点生成一棵子多叉树,当走到 if ( left == 0 && right == 0 ) 时就可以记录下“正确的组合”,否则在 if ( left > right )和if ( left < 0 || right < 0 ) 就返回了。

    2.撤销左括号,意思就是这棵子多叉树已经走完了,下面准备再试试在此位置放置右括号会有哪些“正确的组合”。

    3.右括号同理。

    1. class Solution {
    2. public:
    3. vector res;
    4. vector generateParenthesis(int n) {
    5. string track;
    6. backtrack(n,n,track);//左右括号各有n个
    7. return res;
    8. }
    9. void backtrack(int left,int right,string& track)
    10. {
    11. if(left>right)//左括号剩下的比右括号多,不合法的括号组合
    12. {
    13. return ;//剪枝
    14. }
    15. if(left<0||right<0)
    16. {
    17. return ;//剪枝
    18. }
    19. if(left==0&&right==0)//当两边括号都恰好用完时,得到一个合法的括号组合
    20. {
    21. res.push_back(track);
    22. return ;//子决策树到此为止,也是在剪枝
    23. }
    24. //尝试放一个左括号
    25. track.push_back('(');//做选择
    26. backtrack(left-1,right,track);
    27. track.pop_back();//撤销选择
    28. //尝试放一个右括号
    29. track.push_back(')');//做选择
    30. backtrack(left,right-1,track);
    31. track.pop_back();//撤销选择
    32. }
    33. };

    698. 划分为k个相等的子集

    labuladong 题解思路

    给定一个整数数组  nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。

    示例 1:

    输入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
    输出: True
    说明: 有可能将其分成 4 个子集(5),(1,4),(2,3),(2,3)等于总和。

    示例 2:

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

    思路:回溯算法 

    将n个数划分为k个等和子集,转换下就是,把n个数放进k个集合里,要让k个集合里的数字之和相同。回溯算法要求我们首先找到选择列表,那么从k个集合的视角来看,选择列表就是那n个数,做选择就是选里面的一个数加入桶中,当一个桶中的数字之和达到目标时,就换下一个桶继续,直到所有的桶都装够目标。使用bucket数组来记录每个桶的状态,使用used数组来记录每个数字的使用情况,被用过了就不能再用了。

    解数独这道题类似,这个题的backtrack函数的返回值也要设置为bool类型,因为一旦找到所有桶都装够了目标的情况就要赶紧返回,结束回溯(不然一直回溯下去又会回到原状态)。所以设置bool类型返回值,在每次backtrack时都检验一下返回值是否为true,是true,就结束回溯。

    37.解数独(内含回溯算法重要思路)_{(sunburst)}的博客-CSDN博客

    但是这样做完,时间复杂度还是很高,需要使用备忘录来记录桶的状态和对应的结果,如果在回溯的过程中遇到同样的桶的状态,就直接返回结果,不需要再执行下面的操作,使得时间大大缩短。

    1. class Solution {
    2. public:
    3. unordered_mapbool> memo;
    4. bool canPartitionKSubsets(vector<int>& nums, int k) {
    5. int sum = accumulate(nums.begin(), nums.end(), 0);
    6. if (sum%k!=0)
    7. return false;
    8. int target=sum/k;
    9. int n=nums.size();
    10. vector<int> bucket(k,0);
    11. vector<bool> used(n,false);
    12. return backtrack(k,0,nums,0,used,target);
    13. }
    14. bool backtrack(int k, int cur,vector<int>& nums,int start,vector<bool>& used,int target)
    15. {
    16. if(k==0)
    17. return true;
    18. string state;
    19. for(bool u:used)
    20. {
    21. if(u) state+='1';
    22. else state+='0';
    23. }
    24. if(memo.count(state))
    25. {
    26. return memo[state];
    27. }
    28. if(cur==target)
    29. {
    30. bool res=backtrack(k-1,0,nums,0,used,target);
    31. memo[state]=res;
    32. return res;
    33. }
    34. for(int i=start;isize();i++)
    35. {
    36. if(used[i])
    37. {
    38. continue;
    39. }
    40. if(cur+nums[i]>target)
    41. {
    42. continue;
    43. }
    44. cur+=nums[i];
    45. used[i]=true;
    46. if(backtrack(k,cur,nums,i+1,used,target))
    47. return true;
    48. cur-=nums[i];
    49. used[i]=false;
    50. }
    51. return false;
    52. }
    53. };
  • 相关阅读:
    不容易解的题10.5
    逐步手撕轮播图3(分步教程)
    Redis 三种特殊的数据类型 - Geospatial地理位置 - Hyperloglog基数统计的算法 - Bitmaps位图(位存储)
    java从入门到精通二十五(vue和element 对项目的改进)
    VMware安装CentOS Stream 8以及JDK和Docker
    MySQL基本操作之创建数据表
    TensorFlow基本概念:张量、计算图
    C/C++大学课程信息系统
    【微电网重构】基于粒子群算法实现IEEE33节点系统进行配电网重构 前推回代计算潮流附matlab代码
    下班后用微信处理工作时发病身亡,法院判决:工伤!
  • 原文地址:https://blog.csdn.net/weixin_50437588/article/details/126695607