Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- linked list
- binary search
- 미디움
- 이진트리
- tree
- hash table
- dfs
- 쉬움
- DP
- leetcode
- Depth-first Search
- math
- Python
- Binary
- easy
- matrix
- list
- sorting
- Medium
- Array
- 중간
- two pointers
- binary tree
- 리트코드
- backtracking
- 문자열
- HashTable
- 재귀
- recursive
- string
Archives
- Today
- Total
부부의 코딩 성장 일기
LeetCode 40(Combination Sum II, Python) 본문
1. 문제 링크
Combination Sum II - LeetCode
Can you solve this real interview question? 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 sum to target. Each number in candid
leetcode.com
2. 문제 설명
- 기존 Combination Sum(Leetcode39)의 업그레이드 버전으로, constraint가 하나 더 추가된 문제
- 후보 숫자의 모음인 candidates라는 리스트, target 숫자가 주어졌을 때, candidates numbers의 합이 target이 되는 unique한 조합을 찾아서 반환하는 것
- 여기서 이전 문제와의 차이는, candidates에 있는 elements들은 combination안에서는 딱 한번만 쓰여야 하는 것!
- 예시1) candidates = [10,1,2,7,6,1,5] 이고, target=8 이라면,
- 합해서 8이 되는 조합인, [[1,1,6], [1,2,5], [1,7], [2,6]]이 정답이 된다.
- 이전 문제에서는 무한하게 중복이 허용되었어서, [1,1,1,1,1,1,1,1] 또한 답으로 추가가 되었는데, 이번문제에서는 이것이 제한되어있음
3. 기존 풀이
- 이전 문제를 풀고 gpt한테 개선된 풀이를 물어봤는데, backtracker 알고리즘을 이용해야 잘 풀렸고,
- 해당 풀이에 조건을 더 추가하는 방식으로 코드를 짰다.
- 이전 문제에서는 backtracker가 break되는 조건이
- candidates[i] > remain일 때였고, 이를 제외하고는, backtracker를 또 호출하는 구조의 재귀를 썼었다.
- 이렇게 하면, 무한히 중복해서 호출할 수 있기 때문에,
- candidates_dict이라는 딕셔너리를 두고, elements를 key, frequency를 value로 하는 사전을 만들어두었다.
- 만약 현재까지의 path에서 element의 등장 횟수가, candidates_dict[element]보다 크거나 같다면, 이는 path에 추가하면 안되기 때문에 pass를 하고, 그 이외의 경우에만, backtracker를 호출하는 구조로 짰다
- 이렇게만 하면, 중복이 제거가 되지 않는 이슈가 있었는데 ([1,1,6], [1,1,6]) 처럼 같은 게 두번 등장
- 이걸, if path not in output: output.append(path)를 하게 되면, runtime이 너무 오래 걸렸고,
- 어차피 candidates_dict를 통해 path에 append되는 element의 수는 제한이 되기 때문에,
- backtracker함수를 쓰기 전에, candidates의 중복을 없애주었다. (list(set(candidates))
- runtime 99.6%로 굉장히 높게 통과!
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
candidates_dict = {}
for i in candidates:
if i not in candidates_dict:
candidates_dict[i] = 1
else:
candidates_dict[i] +=1
def backtracker(remain, path, start):
if remain ==0:
output.append(path)
return
for i in range(start,len(candidates)):
if candidates[i] > remain:
break
if path.count(candidates[i]) >= candidates_dict[candidates[i]]:
pass
else:
backtracker(remain-candidates[i], path+[candidates[i]], i)
output=[]
candidates=list(set(candidates))
candidates.sort()
backtracker(target, [], 0)
return output
4. 다른 풀이
- 내가 푼 풀이에서는, dictionary로 저장해야 하는 것, candidates의 중복을 제거해서 backtracker를 호출해야 하는, 부가적인 게 코드에 추가가 되어야 하는데,
- 아래처럼 만약 "직전의 element가 이번과 동일하면" continue를 해서 다음 루프로 가게 추가하면서 기존에 제공된 개수 이상을 사용하며 조합을 만들지 못하도록 강제하여 조금 더 깔끔한 풀이.
- 직관적으로 이해하는데는 약간 시간이 걸렸다.
- 처음에 [1,1,2,4,5], target=6일 때, 그럼 [1,1,4]도 안만들어지는 것 아닌가 생각했는데,
- backtrack([1], 5, 1)이 다시 backtrack을 돌 때는, next_curr = curr 상태에 1이 하나 더 나오기 때문에 추가가 가능하며, 그 이상의 추가일 때만 continue가 걸리는 것
class Solution:
def combinationSum2(self, A: List[int], target: int) -> List[List[int]]:
def backtrack(comb, remain, curr):
if remain == 0:
results.append(comb)
return
for next_curr in range(curr, len(A)):
if next_curr > curr and A[next_curr] == A[next_curr - 1]:
continue
pick = A[next_curr]
if remain - pick < 0:
break
backtrack(comb + [pick], remain - pick, next_curr + 1)
A.sort()
results = []
backtrack([], target, 0)
return results
5. 배운 점
- backtrack에 익숙해지기
- 언제 break를 걸 것인가?
- 언제 recursive가 종료되는가.
'Algorithm > LeetCode' 카테고리의 다른 글
LeetCode 45(Jump Game II, Python) (0) | 2024.01.02 |
---|---|
LeetCode 43(Multiply Strings, Python) (0) | 2024.01.01 |
LeetCode 39(Combination Sum , Python) (0) | 2023.12.30 |
LeetCode 38(Count and Say, Python) (2) | 2023.12.29 |
LeetCode 36(Valid Sudoku, Python) (1) | 2023.12.28 |