-
-
Notifications
You must be signed in to change notification settings - Fork 155
Create 0039-Combination-Sum.md #1104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ajay-dhangar
merged 3 commits into
codeharborhub:main
from
PradnyaGaitonde:PradnyaGaitonde-patch-2
Jun 13, 2024
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
194 changes: 194 additions & 0 deletions
194
dsa-solutions/lc-solutions/0000-0099/0039-Combination-Sum.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
--- | ||
id: Combination Sum | ||
title: Combination Sum(LeetCode) | ||
sidebar_label: 0039-Combination Sum | ||
tags: | ||
- Array | ||
- Backtracking | ||
description: Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. | ||
sidebar_position: 39 | ||
--- | ||
|
||
## Problem Statement | ||
|
||
Given an array of distinct integers `candidates` and a target integer `target`, return a list of all unique combinations of `candidates` where the chosen numbers | ||
sum to `target`. You may return the combinations in any order. | ||
|
||
The same number may be chosen from `candidates` an unlimited number of times. Two combinations are unique if the | ||
frequency | ||
of at least one of the chosen numbers is different. | ||
|
||
The test cases are generated such that the number of unique combinations that sum up to `target` is less than `150` combinations for the given input. | ||
|
||
### Examples | ||
|
||
**Example 1:** | ||
|
||
```plaintext | ||
Input: candidates = [2,3,6,7], target = 7 | ||
Output: [[2,2,3],[7]] | ||
Explanation: | ||
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times. | ||
7 is a candidate, and 7 = 7. | ||
These are the only two combinations. | ||
``` | ||
|
||
**Example 2:** | ||
|
||
```plaintext | ||
Input: candidates = [2,3,5], target = 8 | ||
Output: [[2,2,2,2],[2,3,3],[3,5]] | ||
``` | ||
|
||
**Example 3:** | ||
|
||
```plaintext | ||
Input: candidates = [2], target = 1 | ||
Output: [] | ||
``` | ||
|
||
### Constraints | ||
|
||
- `1 <= candidates.length <= 30` | ||
- `2 <= candidates[i] <= 40` | ||
- All elements of `candidates` are distinct. | ||
- `1 <= target <= 40` | ||
|
||
## Solution | ||
|
||
The Combination Sum problem involves finding all unique combinations in a list of candidates where the candidate numbers sum to a given target. Each number in the list may be used an | ||
unlimited number of times. Below, we discuss three approaches to solve this problem: DFS (Backtracking), Dynamic Programming (Slow), and Dynamic Programming (Fast). | ||
|
||
### Approach 1: DFS (Backtracking) | ||
|
||
#### Explanation: | ||
|
||
1. Use a recursive function to explore all possible combinations. | ||
2. Track the current combination (`cur`) and its sum (`cur_sum`). | ||
3. If `cur_sum` exceeds the target, backtrack. | ||
4. If `cur_sum` equals the target, add the current combination to the result list. | ||
|
||
#### Algorithm | ||
|
||
1. Initialize an empty list `ans` to store the results. | ||
2. Define a recursive function `dfs(cur, cur_sum, idx)` to explore combinations. | ||
3. In `dfs`, if `cur_sum` exceeds the target, return. | ||
4. If `cur_sum` equals the target, add `cur` to `ans` and return. | ||
5. Loop through candidates starting from `idx`, and recursively call `dfs` with updated `cur` and `cur_sum`. | ||
6. Start the DFS with an empty combination, a sum of 0, and starting index 0. | ||
7. Return the list `ans`. | ||
|
||
#### Implementation | ||
|
||
```python | ||
class Solution: | ||
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: | ||
ans = [] | ||
n = len(candidates) | ||
def dfs(cur, cur_sum, idx): | ||
if cur_sum > target: return | ||
if cur_sum == target: | ||
ans.append(cur) | ||
return | ||
for i in range(idx, n): | ||
dfs(cur + [candidates[i]], cur_sum + candidates[i], i) | ||
dfs([], 0, 0) | ||
return ans | ||
``` | ||
|
||
### Complexity Analysis | ||
|
||
- **Time complexity**: O(N^(M/min_cand + 1)), where N is the number of candidates, M is the target, and min_cand is the | ||
smallest candidate. | ||
- **Space complexity**: O(M/min_cand), as the recursion depth can go up to the target divided by the smallest | ||
candidate. | ||
|
||
### Approach 2: Dynamic Programming (Slow) | ||
|
||
#### Explanation: | ||
|
||
1. Use a list `dp` where `dp[i]` contains combinations that sum up to `i`. | ||
2. Build the `dp` list by iterating through all possible sums up to the target. | ||
3. For each sum, update the combinations by considering each candidate. | ||
|
||
#### Algorithm | ||
|
||
1. Create a dictionary `idx_d` to map each candidate to its index. | ||
2. Initialize a list `dp` with empty lists for all sums from 0 to the target. | ||
3. For each sum `i` from 1 to the target: | ||
* Iterate through all previous sums `j` from 0 to `i-1`. | ||
* For each combination in `dp[j]`, update combinations in `dp[i]`. | ||
* Add combinations directly from candidates if they equal `i`. | ||
4. Return the list `dp[-1]`. | ||
|
||
#### Implementation | ||
|
||
```python | ||
class Solution: | ||
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: | ||
idx_d = {val: idx for idx, val in enumerate(candidates)} | ||
n = len(candidates) | ||
dp = [[] for _ in range(target + 1)] | ||
for i in range(1, target + 1): | ||
for j in range(i): | ||
for comb in dp[j]: | ||
start_idx = idx_d[comb[-1]] | ||
for val in candidates[start_idx:]: | ||
if val + j == i: | ||
dp[i].append(comb + [val]) | ||
for candidate in candidates: | ||
if candidate == i: | ||
dp[i].append([candidate]) | ||
return dp[-1] | ||
``` | ||
|
||
### Complexity Analysis | ||
|
||
- **Time complexity**: O(MMM*N), where N is the number of candidates and M is the target. | ||
- **Space complexity**: O(M*M), due to the storage of combinations for each sum up to the target. | ||
|
||
### Approach 3: Dynamic Programming (Fast) | ||
|
||
#### Explanation: | ||
|
||
1. Use a list `dp` where `dp[i]` contains combinations that sum up to `i`. | ||
2. For each candidate, update the combinations for all possible sums up to the target. | ||
|
||
#### Algorithm | ||
|
||
1. Initialize a list `dp` with empty lists for all sums from 0 to the target. | ||
2. For each candidate `c`: | ||
3. For each sum `i` from `c` to the target: | ||
* If `i` equals `c`, add `[c]` to `dp[i]`. | ||
* For each combination in `dp[i - c]`, add `comb + [c]` to `dp[i]`. | ||
4. Return the list `dp[-1]`. | ||
|
||
#### Implementation | ||
|
||
```python | ||
class Solution: | ||
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: | ||
dp = [[] for _ in range(target + 1)] | ||
for c in candidates: | ||
for i in range(c, target + 1): | ||
if i == c: | ||
dp[i].append([c]) | ||
for comb in dp[i - c]: | ||
dp[i].append(comb + [c]) | ||
return dp[-1] | ||
``` | ||
|
||
### Complexity Analysis | ||
|
||
- **Time complexity**: O(MMN), where N is the number of candidates and M is the target. | ||
- **Space complexity**: O(M*M), due to the storage of combinations for each sum up to the target. | ||
|
||
### Conclusion | ||
|
||
In solving the Combination Sum problem: | ||
|
||
1. DFS (Backtracking) is simple and intuitive but can be slow for large inputs due to its exponential time complexity. | ||
2. Dynamic Programming (Slow) uses a structured approach but has higher time complexity (O(MMM*N)) and memory usage. | ||
3. Dynamic Programming (Fast) is more efficient (O(MMN)), making it suitable for larger inputs, though it still uses significant memory. | ||
|
||
Choose DFS (Backtracking) for smaller datasets, DP (Slow) for a structured approach within manageable limits, and DP (Fast) for larger datasets where efficiency is critical. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.