leetcode
leetcode 2901 ~ 2950
组合总和 II

组合总和 II

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 31 ms, 内存: 16.2 MB


/*
 * 思路:
 * 1. 对数组进行排序以便于跳过重复元素。
 * 2. 使用Java Stream来处理回溯算法。
 * 3. 使用Stream来简化集合操作。
 */

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class CombinationSum2Stream {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates); // 排序
        return backtrack(Arrays.stream(candidates).boxed().collect(Collectors.toList()), target, 0);
    }

    private List<List<Integer>> backtrack(List<Integer> candidates, int remain, int start) {
        List<List<Integer>> result = new ArrayList<>();
        if (remain < 0) return result; // 剪枝
        else if (remain == 0) {
            result.add(new ArrayList<>());
            return result;
        } else {
            for (int i = start; i < candidates.size(); i++) {
                if (i > start && candidates.get(i).equals(candidates.get(i - 1))) continue; // 跳过重复元素
                int num = candidates.get(i);
                List<List<Integer>> subResult = backtrack(candidates, remain - num, i + 1);
                for (List<Integer> list : subResult) {
                    list.add(0, num);
                }
                result.addAll(subResult);
            }
        }
        return result;
    }
}

解释

方法:

此题解采用回溯算法解决组合总和问题,关键在于避免重复组合和过滤不合适的路径。首先,将输入数组排序,这有助于后续去重以及提前终止不可能的路径。回溯的过程从数组的起始位置开始,并追踪当前组合的总和。对于每一个元素,算法首先检查是否与前一个元素相同,以避免生成重复的组合。若元素可加入当前路径,算法将其添加到路径中,并递归调用自身,继续探索后续元素。一旦路径中的数字总和超过目标值,或成功匹配目标值,则递归终止。

时间复杂度:

O(2^n)

空间复杂度:

O(n + 解的数量)

代码细节讲解

🦆
为什么在回溯算法中需要对候选数组进行排序?排序对于算法的逻辑和性能有什么具体的影响?
在回溯算法中对候选数组进行排序主要有两个目的:一是为了方便去重,二是为了优化性能。排序后,相同的元素会被集中在一起,这使得在深度优先搜索的过程中,可以通过简单的比较相邻元素来避免选择重复的组合。此外,排序后的数组可以更早地终止搜索:如果当前组合的总和已经超过目标值,由于数组是有序的,后续的元素只会让总和更大,因此可以立即停止这一分支的搜索,从而减少不必要的计算,提高算法效率。
🦆
题解中提到避免生成重复组合需要检查当前元素是否与前一个元素相同。这种方法在哪些情况下可能会失败,是否总是有效?
这种方法通常在候选数组已经排序的情况下是有效的,因为只有在数组有序时,相同的元素才会排列在一起,从而通过比较当前元素与前一个元素是否相同来避免重复。如果候选数组未排序,这种方法则会失效。此外,这种去重策略仅在避免在相同的递归层级上使用重复元素时有效,不影响不同层级间的元素选择。因此,在实现时需要确保跳过重复元素的条件仅在当前层级有效,即 `if i > startIndex and candidates[i] == candidates[i-1]`。
🦆
在回溯过程中,为什么在元素选择后立即检查总和是否超过目标值,这种即时剪枝的策略有什么优势?
这种即时剪枝的策略可以显著提高算法的效率。在回溯过程中,一旦当前的组合总和已经超过目标值,继续添加更多元素只会使总和进一步增大,这明显是无效的。通过立即终止这一路径的搜索,可以避免进行无效的递归调用,从而减少执行时间和计算资源的消耗。这种策略尤其在目标值相对较小而候选数组中包含较大数字时非常有效,可以快速地排除大量不可能满足条件的组合。

相关问题