关于数组遍历过程中的去重问题

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是为了从第二个元素开始不允许在有重复,因为在当前层的第二轮循环开始,每个元素都属于不同的元组的集合了。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值