?LeetCode刷題實戰(zhàn)40:組合總和 II
算法的重要性,我就不多說了吧,想去大廠,就必須要經(jīng)過基礎知識和業(yè)務邏輯面試+算法面試。所以,為了提高大家的算法能力,這個公眾號后續(xù)每天帶大家做一道算法題,題目就從LeetCode上面選 !
今天和大家聊的問題叫做?組合總和 II,我們先來看題面:
https://leetcode-cn.com/problems/combination-sum-ii/
Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.
Each number in candidates may only be used once in the combination.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
題意
樣例
示例?1:
輸入: candidates =?[10,1,2,7,6,1,5], target =?8,
所求解集為:
[
??[1, 7],
??[1, 2, 5],
??[2, 6],
??[1, 1, 6]
]
示例?2:
輸入: candidates =?[2,5,2,1,2], target =?5,
所求解集為:
[
? [1,2,2],
? [5]
]
題解
回溯算法 + 剪枝
本題解答作者:liweiwei1419 https://leetcode-cn.com/problems/combination-sum-ii/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-m-3/
使用 哈希表 天然的去重功能,但是編碼相對復雜;
這里我們使用和第 39 題和第 15 題(三數(shù)之和)類似的思路:不重復就需要按 順序 搜索, 在搜索的過程中檢測分支是否會出現(xiàn)重復結(jié)果 。注意:這里的順序不僅僅指數(shù)組 candidates 有序,還指按照一定順序搜索結(jié)果。


import?java.util.ArrayDeque;
import?java.util.ArrayList;
import?java.util.Arrays;
import?java.util.Deque;
import?java.util.List;
public?class?Solution?{
????public?List> combinationSum2(int[] candidates, int?target) {
????????int?len = candidates.length;
????????List> res = new?ArrayList<>();
????????if?(len == 0) {
????????????return?res;
????????}
????????// 關鍵步驟
????????Arrays.sort(candidates);
????????Dequepath = new?ArrayDeque<>(len);
????????dfs(candidates, len, 0, target, path, res);
????????return?res;
????}
????/**
?????* @param?candidates 候選數(shù)組
?????* @param?len 冗余變量
?????* @param?begin 從候選數(shù)組的 begin 位置開始搜索
?????* @param?target 表示剩余,這個值一開始等于 target,基于題目中說明的"所有數(shù)字(包括目標數(shù))都是正整數(shù)"這個條件
?????* @param?path 從根結(jié)點到葉子結(jié)點的路徑
?????* @param?res
?????*/
????private?void?dfs(int[] candidates, int?len, int?begin, int?target, Dequepath, List ?{> res)
????????if?(target == 0) {
????????????res.add(new?ArrayList<>(path));
????????????return;
????????}
????????for?(int?i = begin; i < len; i++) {
????????????// 大剪枝:減去 candidates[i] 小于 0,減去后面的 candidates[i + 1]、candidates[i + 2] 肯定也小于 0,因此用 break
????????????if?(target - candidates[i] < 0) {
????????????????break;
????????????}
????????????// 小剪枝:同一層相同數(shù)值的結(jié)點,從第 2 個開始,候選數(shù)更少,結(jié)果一定發(fā)生重復,因此跳過,用 continue
????????????if?(i > begin && candidates[i] == candidates[i - 1]) {
????????????????continue;
????????????}
????????????path.addLast(candidates[i]);
????????????// 調(diào)試語句 ①
????????????// System.out.println("遞歸之前 => " + path + ",剩余 = " + (target - candidates[i]));
????????????// 因為元素不可以重復使用,這里遞歸傳遞下去的是 i + 1 而不是 i
????????????dfs(candidates, len, i + 1, target - candidates[i], path, res);
????????????path.removeLast();
????????????// 調(diào)試語句 ②
????????????// System.out.println("遞歸之后 => " + path + ",剩余 = " + (target - candidates[i]));
????????}
????}
????public?static?void?main(String[] args)?{
????????int[] candidates = new?int[]{10, 1, 2, 7, 6, 1, 5};
????????int?target = 8;
????????Solution solution = new?Solution();
????????List> res = solution.combinationSum2(candidates, target);
????????System.out.println("輸出 => "?+ res);
????}
}
上期推文:
