回溯算法
定义
在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。 若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。 而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。
主要策略
保存当前步骤,如果是一个解就输出;维护状态,使搜索路径(含子路径)尽量不重复。必要时,应该对不可能为解的部分进行剪枝(pruning)。
leetcode例题
22. Generate Parentheses
- 给出n个括号的排列组合,Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.[ “((()))”, “(()())”, “(())()”, “()(())”, “()()()”]
- 思路:每个位置都进可以放 ( 或 ) ,但当此位置前没有单独的 ( 时,不能放置 ),建立一个变量记录没有配对的 ( 个数,然后递归求解。
|
|
39. Combination Sum
- Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.The same repeated number may be chosen from candidates unlimited number of times.
- 此类问题的关键是若满足条件,则将解保存,否则需正确回溯到上一步空间状态,我的思路是:对于第i个元素,有放与不放2种状态,则用递归方式依次搜索着2种状态,另外将数组预先排序,可以降低搜索时间。需要吐槽的是,自己的代码功力还是太弱鸡,开始以为递归的话,sums数组在函数中会被修改,好像就递归不起来了,于是传了个虚参,后来发现可以用实参。然后刚开始考虑,对于第i个元素,我居然写出3种状态来了,然后result最后也传了个虚参,submit后,输出空结果,检查了半天,发现result前面少写了个&。最后总共花了一个多小时,效率太慢了。
|
|
40. Combination Sum II
- Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.Each number in candidates may only be used once in the combination.
- 大体思路与上题一样,感觉没什么区别,就是一个元素只能用1次了,然后我啪啪啪一会就写完,一运行发现结果错了,仔细检查发现,重复的元素会造成干扰,如[1,1,2,3,4],target=3,我的结果是[[1,2],[1,2]],这两个1分别是第一、第二个1,然后想到,要么输出结果后对res数组去重,但这样复杂度太高了,于是第二步想用哈希表,将结果插入res数组时先在哈希表中搜索一下,但我哈希表用的不熟悉,写了一会,用的unordered_map<vector
,int>,结果还是报错了,不知道是不是vector 不能做键值,于是用了第三种方法,思路是:对于第i个元素,有放与不放2种状态,但对于重复元素而言(比较candidates[i]==candidates[i+1]),仅对如果放,按以前一样递归搜索,不放则递归搜索下一个不重复元素。还是以[1,1,2,3,4]为例,如果第一个1放,则考虑第二个1放不放,如果第一个1不放,则考虑第三个元素2放不放,这样便不会出现2个一放第一个和放第二个重复的情况。 12345678910111213141516171819202122232425262728293031323334class Solution {public:vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {vector<vector<int>> res;vector<int> vec1;if(candidates.size()==0) return res;sort(candidates.begin(),candidates.end());getSum(candidates, target,res,vec1,0);return res;}void getSum(vector<int>& candidates, int target,vector<vector<int>> &res,vector<int> &vec1,int begin){if(candidates[begin]<target&&begin<candidates.size()-1){vec1.push_back(candidates[begin]);getSum(candidates, target-candidates[begin],res,vec1,begin+1);vec1.pop_back();int i=1;while(begin+i<candidates.size()-1){if(candidates[begin]<candidates[begin+i])break;i++;}getSum(candidates, target,res,vec1,begin+i);}else if(candidates[begin]==target){vec1.push_back(candidates[begin]);res.push_back(vec1);vec1.pop_back();}}};
46. Permutations
- Given a collection of distinct integers, return all possible permutations.
- 递归问题
|
|
51. N-Queens
- N皇后问题,皇后可以直走与走对角线。
- 这里用python写的代码,还遇到一些坑,因为python的类用的不熟练,调用类内调用成员函数要用self.fun(),感觉自己写的很啰嗦,写了40+行,评论区大神明明10+行就能写完,而且写完还花很多时间调bug。
- 另外N皇后问题又如下特殊性质:如果1皇后坐标为(x,y),则其他皇后不能出现在(p,q),p与q满足:p+q!=x+y and p-q != x-y,这2个关系式分别对应2种对角线情况。
|
|
评论区解法:
52. N-Queens II
- N皇后问题输出解的个数
- 基本和上题一样
|
|
78. Subsets
- Given a set of distinct integers, nums, return all possible subsets (the power set).Note: The solution set must not contain duplicate subsets.
- 递归问题
|
|
90. Subsets II
- Given a collection of integers that might contain duplicates, nums, return all possible subsets (the power set).
- 递归问题
|
|