5. 四数相加
题目链接:454. 四数相加 II - 力扣(LeetCode)
思路一:四重循环暴力枚举——超时
Code
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int cnt = 0;
for(int i = 0; i < nums1.length; i ++){
for(int j = 0; j < nums2.length; j ++){
for(int k = 0; k < nums3.length; k ++){
for(int l = 0; l < nums4.length; l ++){
if(nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0){
cnt ++;
}
}
}
}
}
return cnt;
}
}
思路二:两重循环枚举 + 哈希表,分组枚举,将四重循环优化到两重
a + b + c + d = 0
- 循环枚举
nums1,nums2
将a + b
存入哈希表并统计次数,mp<a+b, cnt>
- 枚举
nums3 nums4
进行判断,由a + b + c + d = 0
即a+b = 0 - (c+d)
,若先前nums1 nums2
存在a+b
使得a+b == -(c+d)
即说明a + b + c + d = 0
(判断某个集合中的元素是否出现过——哈希表),那么答案cnt += value
之所以要哈希表要记录
a+b
的次数,是因为有多种结果,方便我们统计答案:nums1[x1] + nums2[x2] = 5
nums1[x3] + nums2[x4] = 5
当枚举
nums3 nums4
时,若nums3[x5] + nums4[x6] = -5
,那么本次就用两种答案:
- x1 x2 x5 x6
- x3 x4 x5 x6
这样时间复杂度就从(n^4) --> 2次(n^2)
Code
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer, Integer> mp = new HashMap<>();
// 枚举 a+b
for(int a : nums1){
for(int b : nums2){
mp.put(a + b, mp.getOrDefault(a + b, 0) + 1);
}
}
// 枚举 c + d
int cnt = 0;
for(int c : nums3){
for(int d : nums4){
int tmp = 0 - (c + d);
if(mp.containsKey(tmp)){
cnt += mp.get(tmp);
}
}
}
return cnt;
}
}
6. 赎金信
思路:哈希表计数
串1
能由串2
构成:串2中对应串1的字母个数要>=串1的字母个数
- 时间复杂度:
O(n)
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
if(ransomNote.length() > magazine.length()) return false;
int[] cnt1 = new int[26];
int[] cnt2 = new int[26];
// 计数
for(int i = 0;i < ransomNote.length(); i ++) cnt1[ransomNote.charAt(i) - 'a'] ++;
for(int i = 0;i < magazine.length(); i ++) cnt2[magazine.charAt(i) - 'a'] ++;
// 能构成:串2中对应串1的字母个数要>=串1的字母个数
for(int i = 0;i < ransomNote.length(); i ++){
int idx = ransomNote.charAt(i) - 'a';
if(cnt2[idx] < cnt1[idx])
return false;
}
return true;
}
}
7. 三数之和
其实这道题目使用哈希法并不十分合适,因为在去重的操作中有很多细节需要注意!
思路:排序 + 循环枚举a
, 双指针枚举b 和 c
(固定a
,双指枚举b c
判断和)
- 排序
- 外层枚举
i(a)
,里边双指针left(i+1开始) right
枚举b c
,若sum = a+b+c
> 0
,那么right --
< 0
,那么left ++
= 0
,记录答案
由于结果集不能冲重复,因此要注意去重剪枝,跳过重复的结果集。
对a去重:
// 对a去重
if(i > 0 && nums[i] == nums[i - 1]) continue;
为啥不是
nums[i] == nums[i + 1]
然后continue
呢?
- 要是这样的话,有的结果集就直接pass过去了,比如
{-1 - 1 2}
,当i
在第一个位置,left在2,right在3,nums[i] == nums[i + 1]
这个结果直接跳过了
- 时间复杂度:
O(n^2)
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for(int i = 0; i < nums.length; i ++){
if(nums[i] > 0){// 排序了 a要是大于0 后面也都是大于0 那肯定不行
return res;
}
// 对a去重
if(i > 0 && nums[i] == nums[i - 1]) continue;
int left = i + 1;
int right = nums.length - 1;
// 双指针枚举 b 和 c
while(left < right){
int sum = nums[i] + nums[left] + nums[right];
if(sum > 0) right --;
else if(sum < 0) left ++;
else{
// 拿到第一次的 a b c
res.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 对 b c去重
while(left < right && nums[left] == nums[left + 1]) left ++;
while(left < right && nums[right] == nums[right - 1]) right --;
// 找到答案时,双指针同时收缩 为下一组答案做好准备
left ++;
right --;
}
}
}
return res;
}
}
8. 四数之和
思路:和三数之和一样的解题思路,只不过多了一层循环,固定枚举a b
,双指针枚举c d
,由于target
是任意的因此要额外注意剪枝的细节,同时结果集不能重复,因此也要对a b c d
进行去重操作!!
剪枝:
// 对a剪枝:注意不单单是nums[k] > target就剪掉了因为tag任意的 tag = -10 {-3 -4 -2 -1}
if(nums[k] > target && nums[k] >= 0){// 直接返回
break;
}
// 二级剪枝
if(nums[i] + nums[k] > target && nums[i] + nums[k] >= 0){
break;
}
- 时间复杂度:
O(n^3)
Code
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
// 固定枚举 ab 双指针枚举cd 注意去重
for(int k = 0; k < nums.length; k ++){// a
// 对a剪枝:注意不单单是nums[k] > target就剪掉了因为tag任意的 tag = -10 {-3 -4 -2 -1}
if(nums[k] > target && nums[k] >= 0){// 直接返回
break;
}
// 对a去重
if(k > 0 && nums[k] == nums[k - 1]){
continue;
}
// 枚举b
for(int i = k + 1; i < nums.length; i ++){
// 二级剪枝
if(nums[i] + nums[k] > target && nums[i] + nums[k] >= 0){
break;
}
// 对b去重
if(i > k + 1 && nums[i] == nums[i - 1]){
continue;
}
// 双指针枚举 c d
int left = i + 1;
int right = nums.length - 1;
while(left < right){
long sum = (long)(nums[k] + nums[i] + nums[left] + nums[right]);
if(sum < target) left ++;
else if(sum > target) right --;
else {
res.add(Arrays.asList(nums[k] , nums[i] , nums[left] , nums[right]));
// 对 c d去重
while(left < right && nums[left] == nums[left + 1]) left ++;
while(left < right && nums[right] == nums[right - 1]) right --;
// 为下一次做准备
left ++;
right --;
}
}
}
}
return res;
}
}