今天的简单题花样很多,值得瞅瞅
1、看题
题目很简单,解决方案也很好想。
而且,从最朴素的到复杂的思路因有尽有。
2、审题
额……
唯一需要注意的大概只有:初始数组nums[]
是无序。
而由于四元组有下标大小的限制,所以不能使用排序。
当然也无需使用排序就是了。
3、思路
好了,由于题目规定4 <= nums.length <= 50
,所以最无脑最暴力的四重循环在这题是可用的。
不然怎么叫简单嘛
不过当然,就算是我也不屑于就这么使用暴力解法。
所以我选择换个法子用暴力。
由于题目规定了四个数的下标必须满足a < b < c < d
,所以很明显,各个指针之间有很严格的边界限制。
再加上规定nums[a] + nums[b] + nums[c] == nums[d]
,所以当d
确定后,题目就变成了在nums[0~d]
中选取三个数,来保证nums[a] + nums[b] + nums[c] == nums[d]
。
然后进一步,当我们在nums[0~d]
中确定了c
后,题目就变成了在nums[0~c]
中选取两个数,来保证nums[a] + nums[b] == nums[d] - nums[c]
。
以此类推,思路就成形了。
4、开工
public int countQuadruplets(int[] nums) {
int count = 0;
for (int i = 3; i < nums.length; i++) {
//查询剩余0~i-1范围中能否有3个数相加,结果为nums[i]
count += find(nums, i - 1, nums[i], 2);
}
return count;
}
public int find(int[] nums, int end, int target, int deep) {
int count = 0;
for (int i = end; i >= deep; i--) {
if (deep > 0) {
//当深度不为0时,减去当前数字,并在剩余数组中继续查询
if (nums[i] >= target) {
continue;
}
//查询子数组中,满足情况的数量
count += find(nums, i - 1, target - nums[i], deep - 1);
} else {
//当深度为0时,完全匹配target
if (nums[i] == target) {
count++;
}
}
}
return count;
}
整体就是如此,在首先确认了d后,再在剩余的空间中递归确认满足条件的a、b、c的数量即可。
5、解读
整体实现和思路描述的一致。
在find()
方法中,我使用了deep
来控制递归深度。
同时,由于我们明确知道要选取的数字的数量,便也知道了各个数字的左边界(如d>=3、c>=2这样),所以,我也用deep
来控制了各个数字的左边界,而右边界自然是由上一个选择的数字决定的。
整个实现没有什么难点,所以不过多解释。
还有更好的解法,大家多动脑想想那些吧
6、提交
比预期的要好,毕竟还是递归的思路,本以为并不会快多少。
时间复杂度不会算。。。
7、来学大佬们的吧
大牛们的解法真的是百花齐放百家争鸣
挑几个有代表性的吧。
首先还是官解。
官解提供了三种解法,从最朴实无华的暴力循环,到依赖哈希表的优化循环。
可以说,每一种都是在前一种解法的接触上升级的,而效果也越发明显。时间复杂度从O(N4)提升到了O(N2)
之后是一种多维背包解法,看了很多大佬的,但个人觉得最详细的还是三叶大佬的。
8、总结
想不到简单题也能被各位大佬玩出花来。
今天也可以说是收获颇丰啊。