ans:一般在一个集合里反复操作,用。在多个独立集合里,不能用。
ans:i表示,在每一层的开始,往深处递归,都可以再选自身。就是允许有重复的值
i+1表示,在每一层开始,往深处递归时,都得在自身基础上往前走一个。就是不允许取自身
可以从两个维度考虑去重:1、树枝去重。2、树层去重。
1、树枝去重:比如(1,1,2),向下递归的时候,如果开始用第二个1,发现第一个1用过了,就continue了,会漏掉112,这个结果(题目不允许重复使用一个位置上的相同元素) used[i - 1] == true,说明同一树枝candidates[i - 1]使用过 2、树层去重:(1,1,2),想右遍历的时候,如果遍历到第二个1,发现第一个1使用过(但是used是false,是由于递归回溯的时候,又初始化了),又因为是排过序的,所以第二个1之后遍历到的结果,肯定已经被第一个1包含了,即再取就重复了。
used[i - 1] == false,说明同一树层candidates[i - 1]使用过.
2、树层去重:(1,1,2),想右遍历的时候,如果遍历到第二个1,发现第一个1使用过(但是used是false,是由于递归回溯的时候,又初始化了),又因为是排过序的,所以第二个1之后遍历到的结果,肯定已经被第一个1包含了,即再取就重复了。
used[i - 1] == false,说明同一树层candidates[i - 1]使用过。
!!!子集类问题去重一定要先排序!!!
1、记录每个下标的元素是否被使用过,仅在一层递归时,会变化,当回溯后(返回上一层,会被初始化)。
注意:why定义全局变量,而不像set一样是局部的(可以局部),主要还是逻辑不一样。先排好序的used是判断位置,而set是判断元素值。保证了树枝可重复,树层不重复。
得先排序
if(i > startindex && candidates[i] == candidates[i-1])
i < startindex表示深度递归,往下递归。
i > startindex 表示同一层级的遍历。以第二层为例:假设第二层的第一个元素深度递归完了之后,i++,遍历第二层的第二个元素,此时startindex并没有变化,还是第一层传上来的0+1=1,但是i已经变成2了,如果和前面一个元素相同,就直接略过了。
unordered_set
注意:set不能定义到全局变量,因为,设定的set是管理记录某一个节点下面的同一层。定义到全局变量,就是记录整个树,包括树枝和树层。可得,第一个图已经记录了2,到下一层就不会再选2了。导致错误。而如果是局部set,到第二个时,会重新设一个set来记录,保证了树枝上的可重复选取值相同但位置不同的元素。所以也不能同used一样 insert 和 erase。

使用set去重,set不用参与到回溯代码。因为会在每一层重新定义一个set。set只针对于每一层。
如果范围小,可直接用数组,省时省力。
- result.push_back(path);
- unordered_set<int> uset;
- for (int i = startIndex; i < nums.size(); i++) {
- if (uset.find(nums[i]) != uset.end()) {
- continue;
- }
- uset.insert(nums[i]);
- path.push_back(nums[i]);
- backtracking(nums, i + 1);
- path.pop_back();
- }
- }
在求和问题中,排序之后加剪枝是常见的套路!
ps:
子集是收集树形结构中树的所有节点的结果。
组合、分割问题是收集树形结构中叶子节点的结果。