1、三个数求和的去重问题(LC的第15题)
题目描述:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
示例:
给定数组 nums =[-4, -1, -1, 0, 1, 2],
满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
// 找出a + b + c = 0
// a = nums[i], b = nums[left], c = nums[right]
for (int i = 0; i < nums.length; i++) {
// 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
if (nums[i] > 0) {
return result;
}
if (i > 0 && nums[i] == nums[i - 1]) { // 去重a
continue;
}
int left = i + 1;
int right = nums.length - 1;
while (right > left) {
int sum = nums[i] + nums[left] + nums[right];
if (sum > 0) {
right--;
} else if (sum < 0) {
left++;
} else {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
right--;
left++;
}
}
}
return result;
}
}
关于解释 nums[i] == nums[i - 1]时的去重问题
首先第一个-1 收集到的元素集合不详述过程,在i 指向第二个-1时,left和right分别指向0和2。其实我们在这时可以注意到从第二个-1 开始收集数据元素时,它的left和right的移动区间是第一个-1 的left和right移动区间的子区间。所以第二个-1 所收集到的元素集合一定在第一个-1 收集时已经存在的,已不用在对此元素进行集合元素收集。
2、回溯中元素组合的去重问题(LC的第40题)
题目描述:给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明: 所有数字(包括目标数)都是正整数。解集不能包含重复的组合。
- 示例:
- 输入: candidates = [10,1,2,7,6,1,5], target = 8,
- 所求解集为:[[1,7], [1,2,5], [2,6], [1,1,6]]
void backtrack_40(int[] candidates, int target, int sum, int startIndex, boolean currentLevel){
if(sum == target){
result_40.add(new ArrayList<>(path_40));
return;
}
if(sum > target)
return;
for(int i = startIndex; i < candidates.length && sum <= target - candidates[i]; i++){
if(i >= 1 && currentLevel && candidates[i] == candidates[i - 1])
continue;
currentLevel = true;
path_40.add(candidates[i]);
sum += candidates[i];
backtrack_40(candidates, target, sum, i + 1, false);
sum -= candidates[i];
path_40.remove(path_40.size() - 1);
}
}
在回溯的逻辑树型结构中,同一层的各个节点表示不同的元组集合,而从树的根到叶的路径中的各个节点表示的是同一元组的不同元素,我们要的是组间去重(即不同元组集合不能相同),而不是组内去重。
在此题中,我们注意到去重不在是上面提到的仅仅判断nums[i] == nums[i - 1],因为这个去重的不仅仅是组间,还有组内。
- 在不考虑递归情况下,for循环的整体是对同一层节点的遍历,用一个布尔变量表示是否为当前层。
- 在向下递归过程中将当前层currentLevel这个变量设为false,故即使在下一层开始时,第一轮循环过程中即便有与之前相同的元素也进行收集,因为第一个元素的遍历与上一层递归的元素它们在同一个元组集合。
- 将currentLevel设置为true是为了从第二个元素开始不允许在有重复,因为在当前层的第二轮循环开始,每个元素都属于不同的元组的集合了。