11import java .util .ArrayList ;
2+ import java .util .Arrays ;
23import java .util .HashSet ;
34import java .util .List ;
45import java .util .Set ;
1112 */
1213class Solution {
1314
15+ /**
16+ * 중복순열을 구하는 부분은 2번 풀이와 동일하지만,
17+ * 후보배열을 정렬하고, 후보 선택 시 이전 값과 같거나 큰 후보만을 선택하기에
18+ * 동일 요소가 존재하는 배열을 중복해서 만들지 않음.
19+ * 또한 Set 같은 저장위치를 제거해서 메모리 최적화.
20+ *
21+ * Runtime: 5 ms (Beats 8.49%)
22+ * Memory: 45.77 MB (Beats 16.78%)
23+ * Space Complexity: O(N)
24+ * - 사용된 후보를 저장하는 배열 O(N)
25+ * > O(N)
26+ * Time Complexity: O(2^N/N!)
27+ * - 후보 하나를 선택해서 target과 비교 => O(2^N)
28+ * - target이 0이 되거나 누적 값이 더 커질 때까지 스택을 쌓기
29+ * - 최대 누적할 수 있는 횟수는 40 / 2 = 20회 => O(20) = O(M)
30+ * - 다만, 중복배열 생성을 방지하는 트릭을 추가함 O(1/N!)
31+ * > O(2^N/N!)
32+ *
33+ * @param candidates
34+ * @param target
35+ * @return
36+ */
37+ public List <List <Integer >> combinationSum (int [] candidates , int target ) {
38+ List <List <Integer >> ans = new ArrayList <>();
39+ List <Integer > acc = new ArrayList <>();
40+ Arrays .sort (candidates );
41+
42+ makecombination (ans , acc , target , candidates , 0 );
43+ return ans ;
44+ }
45+
46+ /**
47+ * 이전에 더했던 후보와 같거나 큰 후보만 더한다.
48+ * set: return용 조합 저장
49+ * acc: 사용 후보 누적
50+ * usedCount: 후보별 사용된 횟수 저장
51+ * target: 만들 수
52+ */
53+ private void makecombination (List <List <Integer >> set , List <Integer > acc , int target , int [] candidates ,
54+ int idx ) {
55+ // 완성된 경우 리턴하기
56+ if (target == 0 ) {
57+ List <Integer > tmp = new ArrayList <>(acc ); // 깊은 복사
58+ set .add (tmp );
59+ return ;
60+ } else if (target < 0 || idx >= candidates .length ) { // 타겟이 0 보다 작은 경우 스킵
61+ return ;
62+ }
63+
64+ // 현재 인덱스 후보 사용
65+ acc .add (candidates [idx ]);
66+ makecombination (set , acc , target - candidates [idx ], candidates , idx );
67+ acc .remove (acc .size () - 1 ); // 마지막 요소 제거
68+
69+ // 다음 인덱스 후보 사용
70+ makecombination (set , acc , target , candidates , idx + 1 );
71+ return ;
72+ }
73+
1474 /**
1575 * candidates의 후보들은 중복 사용이 가능하다.
1676 * 모든 후보는 구별된다.=> 중복이 없다.
@@ -21,11 +81,10 @@ class Solution {
2181 * - 사용되는 후보를 Set으로 관리하기 => O(N)
2282 * - 후보가 사용된 횟수를 관리하기 => O(K)
2383 * > O(N) + O(K)
24- * Time Complexity: O(N)
25- * - 후보 하나를 선택해서 target과 비교 => O(N^M)
26- * - target이 0이 되거나 누적 값이 더 커질 때까지 스택을 쌓기
27- * - 최대 누적할 수 있는 횟수는 40 / 2 = 20회 => O(20) = O(M)
28- * > O(N)*O(M)
84+ * Time Complexity: O(2^N)
85+ * - 후보 하나를 선택해서 target과 비교
86+ * - target이 0이 되거나 누적 값이 더 커질 때까지 스택을 쌓기 => O(2^N)
87+ * > O(2^N)
2988 */
3089 public List <List <Integer >> combinationSum2 (int [] candidates , int target ) {
3190 Set <List <Integer >> set = new HashSet <>();
@@ -38,6 +97,7 @@ public List<List<Integer>> combinationSum2(int[] candidates, int target) {
3897 }
3998
4099 /**
100+ * target을 만드는 순열을 만든다.. => 중복을 포함하기 때문에 중복 연산이 많다..
41101 * set: return용 조합 저장
42102 * acc: 사용 후보 누적
43103 * usedCount: 후보별 사용된 횟수 저장
0 commit comments