You are given an integer array nums
and an integer k
. The goal is to determine if it's possible to divide the array into k
non-empty subsets such that the sum of each subset is equal.
Details:
nums
contains positive integers.k
is a positive integer.nums
must belong to exactly one subset.k
subsets must have the same sum.Constraints:
1 <= k <= nums.length <= 16
1 <= nums[i] <= 10^4
Examples:
nums = [4, 3, 2, 3, 5, 2, 1], k = 4
true
(5), (1, 4), (2, 3), (2, 3)
with equal sums.nums = [1, 2, 3, 4], k = 3
false
How would you approach solving this problem efficiently? Can you describe the algorithm and provide a code example?
Given an integer array nums
and an integer k
, determine if it is possible to divide this array into k
non-empty subsets such that the sum of each subset is equal.
A brute-force approach would involve generating all possible subsets of the input array and checking if we can form k
subsets with equal sums. This approach involves generating all possible combinations of elements, leading to exponential time complexity.
nums
.k
subsets such that each subset is non-empty and has the same sum.This approach is highly inefficient and not practical for larger input sizes.
Time Complexity: O(2n * n), where n is the number of elements in the array.
Space Complexity: O(n), for storing the subsets.
We can use a backtracking approach with pruning to solve this problem more efficiently.
Calculate the target sum: Calculate the sum of all elements in nums
. If the sum is not divisible by k
, return false
immediately because it is impossible to form k
equal sum subsets.
Calculate the target subset sum: Divide the total sum by k
to find the target sum for each subset.
Backtracking:
subsets
of size k
to store the current sum of each subset.nums
to one of the subsets.true
.false
.k
is 1, the answer is always true
.nums
is not divisible by k
, the answer is false
.nums
is greater than the target subset sum, the answer is false
.class Solution {
public boolean canPartitionKSubsets(int[] nums, int k) {
int sum = 0;
for (int num : nums) {
sum += num;
}
if (sum % k != 0) {
return false;
}
int target = sum / k;
boolean[] used = new boolean[nums.length];
return canPartition(nums, k, 0, 0, target, used);
}
private boolean canPartition(int[] nums, int k, int subsetSum, int start, int target, boolean[] used) {
if (k == 0) {
return true;
}
if (subsetSum == target) {
return canPartition(nums, k - 1, 0, 0, target, used);
}
for (int i = start; i < nums.length; i++) {
if (!used[i] && subsetSum + nums[i] <= target) {
used[i] = true;
if (canPartition(nums, k, subsetSum + nums[i], i + 1, target, used)) {
return true;
}
used[i] = false; // Backtrack
}
}
return false;
}
}
Time Complexity: O(k * 2n). Although it looks exponential, the pruning significantly reduces the actual execution time. In the worst-case scenario, we explore a large portion of possible subsets.
Space Complexity: O(n), where n is the length of nums
. Dominated by the used
array and recursion depth.