Skip to content

Commit 1d9e5cb

Browse files
committed
0416-partition-equal-subset-sum.md Added 7 languages' solutions.
1 parent 47e7d43 commit 1d9e5cb

3 files changed

Lines changed: 251 additions & 2 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
.idea/
1+
.idea/
2+
temp/

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Here, I will provide you with **common problem-solving patterns** so that you do
99

1010
I will also provide you with solutions for many common programming languages, such as **Python, C++, Java, JavaScript, C#, Go, Ruby**, etc.
1111

12-
I have tried my best to write the most concise and efficient code, so I call it my best practice. If you have better solutions, welcome to create an issue or PR.
12+
I have tried my best to write the most concise and efficient code, so I call it my best practice. If you have better solutions, welcome to create an issue or PR!
1313

1414
## How to use this repo?
1515
I have planned a learning route for you. You just need to do the questions in the order they are listed.
@@ -23,5 +23,6 @@ After finishing one category of questions, you can study another category to imp
2323
- [392. Is Subsequence](problems/0392-is-subsequence.md)
2424
- [583. Delete Operation for Two Strings](problems/0583-delete-operation-for-two-strings.md)
2525
- [72. Edit Distance](problems/0072-edit-distance.md)
26+
- [416. Partition Equal Subset Sum](problems/0416-partition-equal-subset-sum.md)
2627

2728
- More LeetCode problems will be added soon...
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
# 416. Partition Equal Subset Sum
2+
LeetCode problem: [416. Partition Equal Subset Sum](https://leetcode.com/problems/partition-equal-subset-sum/)
3+
4+
## LeetCode problem description
5+
> Given an integer array nums, return true if you can partition the array into two subsets such that the sum of the elements in both subsets is equal or false otherwise.
6+
7+
```
8+
Example 1:
9+
10+
Input: nums = [1,5,11,5]
11+
Output: true
12+
Explanation: The array can be partitioned as [1, 5, 5] and [11].
13+
------------------------------------------------------------------------
14+
15+
Example 2:
16+
17+
Input: nums = [1,2,3,5]
18+
Output: false
19+
Explanation: The array cannot be partitioned into equal sum subsets.
20+
21+
------------------------------------------------------------------------
22+
Constraints:
23+
24+
1 <= nums.length <= 200
25+
1 <= nums[i] <= 100
26+
```
27+
28+
## Thoughts
29+
* When we first see this problem, we might want to loop through all subsets of the array. If there is a subset whose sum is equal to `half of the sum`, then return `true`. This can be achieved with a `backtracking algorithm`, but after seeing the constraint `nums.length <= 200`, we can estimate that the program will time out.
30+
* This is actually a `01 knapsack problem`. `01 knapsack problem` belongs to `dynamic programming`. `Dynamic programming` means that the answer to the current problem can be derived from the previous similar problem. Therefore, the `dp` array is used to record all the answers.
31+
32+
* The core logic of the `01 knapsack problem` uses a two-dimensional `dp` array or a one-dimensional `dp` **rolling array**, first **traverses the items**, then **traverses the knapsack in reverse**, then **reference the previous value corresponding to the size of current 'item'**.
33+
* There are many things to remember when using a two-dimensional `dp` array, and it is difficult to write it right at once during an interview, so I won't describe it here.
34+
35+
### Common steps in '01 Knapsack Problem'
36+
These five steps are a pattern for solving `dynamic programming` problems.
37+
38+
1. Determine the **meaning** of the `dp[j]`
39+
* We can use a one-dimensional `dp` **rolling array**. Rolling an array means that the values of the array are overwritten each time through the loop.
40+
* At first, try to use the problem's `return` value as the value of `dp[j]` to determine the meaning of `dp[j]`. If it doesn't work, try another way.
41+
* So, `dp[j]` represents whether it is possible to `sum` the first `i` `nums` to get `j`.
42+
* The value of `dp[j]` is a boolean.
43+
2. Determine the `dp` array's initial value
44+
* Use an example:
45+
```
46+
nums = [1,5,11,5], so 'half of the sum' is 11.
47+
The `size` of the knapsack is `half of the sum`, and the `items` are `nums`.
48+
So after initialization, the 'dp' array would be:
49+
# 0 1 2 3 4 5 6 7 8 9 10 11
50+
# T F F F F F F F F F F F # dp
51+
# 1
52+
# 5
53+
# 11
54+
# 5
55+
```
56+
* You can see the `dp` array size is one greater than the knapsack size. In this way, the backpack size and index value are equal, which helps to understand.
57+
* `dp[0]` is set to `true`, indicating that an empty backpack can be achieved by not putting any items in it. In addition, it is used as the starting value, and the subsequent `dp[j]` will depend on it. If it is `false`, all values of `dp[j]` will be `false`.
58+
* `dp[j] = false (j != 0)`, indicating that it is impossible to get `j` with no `nums`.
59+
60+
3. Determine the `dp` array's recurrence formula
61+
* Try to complete the grid. In the process, you will get inspiration to derive the formula.
62+
```
63+
1. Use the first num '1'.
64+
# 0 1 2 3 4 5 6 7 8 9 10 11
65+
# T F F F F F F F F F F F
66+
# 1 T T F F F F F F F F F F # dp
67+
```
68+
```
69+
2. Use the second num '5'.
70+
# 0 1 2 3 4 5 6 7 8 9 10 11
71+
# T F F F F F F F F F F F
72+
# 1 T T F F F F F F F F F F
73+
# 5 T T F F F T T F F F F F
74+
```
75+
```
76+
3. Use the third num '11'.
77+
# 0 1 2 3 4 5 6 7 8 9 10 11
78+
# T F F F F F F F F F F F
79+
# 1 T T F F F F F F F F F F
80+
# 5 T T F F F T T F F F F F
81+
# 11 T T F F F T T F F F F T
82+
```
83+
```
84+
3. Use the last num '5'.
85+
# 0 1 2 3 4 5 6 7 8 9 10 11
86+
# T F F F F F F F F F F F
87+
# 1 T T F F F F F F F F F F
88+
# 5 T T F F F T T F F F F F
89+
# 11 T T F F F T T F F F F T
90+
# 5 T T F F F T T F F F T T # dp
91+
```
92+
* After analyzing the sample `dp` grid, we can derive the `Recurrence Formula`:
93+
```python
94+
dp[j] = dp[j] or dp[j - nums[i]]
95+
```
96+
4. Determine the `dp` array's traversal order
97+
* `dp[j]` depends on `dp[j]` and `dp[j - nums[i]]`, so we should traverse the `dp` array from top to bottom, then **from right to left**.
98+
* Please think if we can traverse the `dp` array from top to bottom, then `from left to right`? In the `Python` code comments, I will answer this question.
99+
5. Check the `dp` array's value
100+
* Print the `dp` to see if it is as expected.
101+
102+
### Complexity
103+
* Time: `O(n * sum/2)`.
104+
* Space: `O(sum/2)`.
105+
106+
## Python
107+
```python
108+
class Solution:
109+
def canPartition(self, nums: List[int]) -> bool:
110+
sum_ = sum(nums)
111+
if sum_ % 2 == 1:
112+
return False
113+
114+
dp = [False] * ((sum_ // 2) + 1)
115+
dp[0] = True
116+
117+
for num in nums:
118+
for j in range(len(dp) - 1, 0, -1): # If traverse from left to right, the newly assigned value `dp[j - num]` will affect the subsequent `dp[j]`. This is wrong because each `num` can only be used once.
119+
if j < num:
120+
break
121+
dp[j] = dp[j] or dp[j - num]
122+
123+
return dp[-1]
124+
```
125+
126+
## C++
127+
```cpp
128+
class Solution {
129+
public:
130+
bool canPartition(vector<int>& nums) {
131+
auto sum = reduce(nums.begin(), nums.end());
132+
if (sum % 2 == 1)
133+
return false;
134+
135+
auto dp = vector<bool>(sum / 2 + 1);
136+
dp[0] = true;
137+
138+
for (auto num : nums)
139+
for (auto j = dp.size() - 1; j >= num; j--)
140+
dp[j] = dp[j] || dp[j - num];
141+
142+
return dp[dp.size() - 1];
143+
}
144+
};
145+
```
146+
147+
## Java
148+
```java
149+
class Solution {
150+
public boolean canPartition(int[] nums) {
151+
var sum = IntStream.of(nums).sum();
152+
if (sum % 2 == 1)
153+
return false;
154+
155+
var dp = new boolean[sum / 2 + 1];
156+
dp[0] = true;
157+
158+
for (var num : nums)
159+
for (var j = dp.length - 1; j >= num; j--)
160+
dp[j] = dp[j] || dp[j - num];
161+
162+
return dp[dp.length - 1];
163+
}
164+
}
165+
```
166+
167+
## C#
168+
```c#
169+
public class Solution {
170+
public bool CanPartition(int[] nums) {
171+
var sum = nums.Sum();
172+
if (sum % 2 == 1)
173+
return false;
174+
175+
var dp = new bool[sum / 2 + 1];
176+
dp[0] = true;
177+
178+
foreach (var num in nums)
179+
for (var j = dp.Length - 1; j >= num; j--)
180+
dp[j] = dp[j] || dp[j - num];
181+
182+
return dp[dp.Length - 1];
183+
}
184+
}
185+
```
186+
187+
## JavaScript
188+
```javascript
189+
var canPartition = function(nums) {
190+
sum = _.sum(nums)
191+
if (sum % 2 == 1)
192+
return false
193+
194+
dp = Array(sum / 2 + 1).fill(false)
195+
dp[0] = true
196+
197+
for (num of nums)
198+
for (let j = dp.length - 1; j >= num; j--)
199+
dp[j] = dp[j] || dp[j - num]
200+
201+
return dp.at(-1)
202+
};
203+
```
204+
205+
## Go
206+
```go
207+
func canPartition(nums []int) bool {
208+
sum := 0
209+
for _, num := range nums {
210+
sum += num
211+
}
212+
if sum % 2 == 1 {
213+
return false
214+
}
215+
216+
dp := make([]bool, sum / 2 + 1)
217+
dp[0] = true
218+
219+
for _, num := range nums {
220+
for j := len(dp) - 1; j >= num; j-- {
221+
dp[j] = dp[j] || dp[j - num]
222+
}
223+
}
224+
225+
return dp[len(dp) - 1]
226+
}
227+
```
228+
229+
## Ruby
230+
```ruby
231+
def can_partition(nums)
232+
sum = nums.sum
233+
return false if sum % 2 == 1
234+
235+
dp = Array.new(sum / 2 + 1, false)
236+
dp[0] = true
237+
238+
nums.each do |num|
239+
(1..(dp.size - 1)).reverse_each do |j|
240+
break if j < num
241+
dp[j] = dp[j] || dp[j - num]
242+
end
243+
end
244+
245+
return dp[-1]
246+
end
247+
```

0 commit comments

Comments
 (0)