Medium
这题原来的解法不是很好懂,有位拿到google offer的前辈说解法最重要的是“自己觉得好懂 + 面试当场能解释清楚”的。 我觉得很对,自己做得最顺的可能既不是最naive的也不是最优化的,但可以先用自己觉得最理解的方法做,再一步步学会优化即可。
一开始先算出来每分的和是多少,解决一些可以尽早判断t,f的情况。然后sort,如果最小的都比每分大,肯定false. 从后往前遍历去跳过那些本身就等于每分subsum的元素。比如[1,5,5,11]里面的11. 跳过的时候beginIndex--,k--. 然后新建一个size = k的int[] 来存subsets,每个index对应的数就是每一分现在的sum.
helper method需要int[k] subset, int[] nums, int subSum, int index
做参数。这个index就是从后遍历到现在的那个index。我们可以进入helper method就判断这个index是不是小于0,如果是的话,说明我们已经遍历完所有的nums元素,直接return true。 然后我们选现在的这个selected数,让它尝试加入到当前subsets里的每一组。如果某一组加了之后可以partition成功,就返回true. 如果不能,就backtracking.
画一画recursion tree 再分析一下时间空间复杂度:
什么情况canPartition会return false???
class Solution {
public boolean canPartitionKSubsets(int[] nums, int k) {
int sum = 0;
for (int i = 0; i < nums.length; i++){
sum += nums[i];
}
if (sum % k != 0){
return false;
}
int subSum = sum / k;
Arrays.sort(nums);
if (nums[0] > subSum){
return false;
}
int beginIndex = nums.length - 1;
while (beginIndex >= 0 && nums[beginIndex] == subSum){
beginIndex--;
k--;
}
return canPartition(nums, subSum, new int[k], beginIndex);
}
private boolean canPartition(int[] nums, int subSum, int[] subsets, int index){
if (index < 0){
return true;
}
int selected = nums[index];
for (int i = 0; i < subsets.length; i++){
if (subsets[i] + selected <= subSum){
subsets[i] += selected;
if (canPartition(nums, subSum, subsets, index - 1)){
return true;
}
subsets[i] -= selected;
}
}
return false;
}
}