一、什么时候必须用used数组?
结论:给定数组nums里有重复元素,必须用used数组。
1.如[1,2,2,2,3]有重复元素,需要借助used[i]去重第二个以及之后的重复元素(也就是1,2和3之间的两个2);如力扣例题t40,t90,
int used[i] = new used[nums.length];//默认0未用1用,但用完后会归为0;
Arrays.sort(nums);//升序,方便让值一样的元素连续着,
//然后进行去重第二个以及之后的重复元素的判断。
//防止计算重复了以重复元素为首的情况,
//只计算第一个重复元素为首的情况
if(i > 0 && nums[i-1] == nums[i] && used[i-1] == 0)
/*如例子[1,2,2,3],能进到此if语句的,
一定是重复元素里的第二个及以后,
在遍历到后面的2的时候,
发现第一个2曾经进入过path了,
所有以2为首的情况已经计算过了,
所以跳过后面所有以2为首的情况。
*/
continue;
path.addLast(nums[i]);
used[i] = 1;//此时nums[i]已经在path里了,
//所以used[i]为1
backTracking(nums,used);
path.removeLast();
used[i] = 0;//此时nums[i]不在path里了,
//所以used[i]为0,但此时的0与默认0不一样,
//因为此时以nums[i]为首的所有情况已经都在path里了
2.如果给定的nums内无重复元素如[1,2,3],那么用不用used都行。
二、允不允许元素使用自己?
结论:如果结果集不允许元素重复使用自己(注意也许题目中元素有重复,允许使用与自己相等的元素,但不是元素自己复用如t40明确要求candidates数组中的每个数字在每个组合中只能使用一次。),那么分两种情况:
规则2.1有start,则backTracking(nums,i+1);//用i+1避开重复使用自己
规则2.2无start,即代表每次都从0开始遍历,则
if(used[i] == 1)//因为没有start而是从0遍历,
//所以可以往回走,因此可能会遍历到自己,
//但是结果里要保证没有自己(可以有和自己等值的重复元素)
//可以借助used避免用到自己
continue;
注解:t40这道题比较特殊,它因为要求结果集内元素递增(即不能往回走)所以出现了start,因此根据规则2.1,要用i+1来避免使用自己;同时它给定的数组又有重复元素,所以必须用used去掉重复元素的影响。
三、什么时候必须用start?
结论:如果结果集不允许有如[1,3,2]这种非递增的结果只允许有[1,3,4],[1,2,3]等递增结果,那么必须用start来防止往回走。
四、总结
1.数组nums有没有重复元素?
有重复元素就得用used,没有就不必;
2.每个结果内的元素必须是递增的吗?
必须递增就要用start防止往回走,不是必须递增就不用start而是每次让i都从0开始遍历。
3.不允许使用同一个元素多次吗?
不允许就要用start配合i+1或者没有start即每次都从0遍历时判断当used[i]==1时continue来避免同一个元素使用多次。
特别注意
在题目剑指 Offer 38. 字符串的排列中,既允许原始数组有重复元素(也就是说必须用used去掉与自己相同的重复元素),而且允许往回走(也就是说必须没有start),那么就无法同时用if(used[i] == 1) continue;
和if(i > 0 && nums[i-1] == nums[i] && used[i-1] == 0) continue;
来分别防止使用自己和防止使用与自己相同的重复元素。
本题题解