代码随想录算法训练营|哈希表 Part 02
一、第454题.四数相加II
题目
思路
- 不用去重:在不同数组的不同位置
- 暴力解法:四个循环,时间复杂度 O( n4 )
- 哈希法
- 针对四个数组 A B C D ,将 A B 数组对应元素相加,结果存放在一个集合E中;对 C D 进行同样操作,根据结果判断 E 中有无需要的元素
- 数组元素数值无限制,不可用数组
- key储存元素,value储存元素出现次数,选用map
- 时间复杂度为 O(n2)
代码
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int res = 0;
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
//统计两个数组中的元素之和(key),同时统计出现的次数(value),放入map
for(int i : nums1)
{
for(int j : nums2)
{
int sum = i+j;
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
}
//统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数
for(int i : nums3)
{
for(int j : nums4)
{
res += map.getOrDefault(0 - i - j, 0);
}
}
return res;
}
}
二、383. 赎金信
题目
思路
- 只需要关心杂志字符串能否组成赎金信字符串
- 杂志字符只能使用一次(且限制为小写字母)
- 暴力解法
- 遍历杂志、赎金信字符串,看是否有相等的字符,如果有,在赎金信中删除该字符。遍历完成后,检查赎金信字符串是否为
null
,是则返回true
,否则返回false
- 时间复杂度:O(N2)
- 空间复杂度:O(1)
- 遍历杂志、赎金信字符串,看是否有相等的字符,如果有,在赎金信中删除该字符。遍历完成后,检查赎金信字符串是否为
- 哈希法-数组
- 由于均为小写字母,利用长度为26的数组记录杂志字符串中字母出现的次数
- 再用赎金信去验证这个数组是否包含了赎金信字符串所需要的所有字母
- 时间复杂度:O(n)
- 空间复杂度:O(1)
代码
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
if(ransomNote.length() > magazine.length())
{
return false;
}
//定义一个哈希映射数组
int[] record = new int[26];
//遍历
for(char c : magazine.toCharArray())
{
record[c - 'a'] += 1;
}
for(char c : ransomNote.toCharArray())
{
record[c - 'a'] -= 1;
}
//如果数组中存在负数,说明ransomNote字符串中存在magazine中没有的字符
for(int i : record)
{
if(i < 0)
{
return false;
}
}
return true;
}
}
三、15. 三数之和
题目
思路
-
双指针法
- 先对数组进行排序
- 后在
for
循环中,将a
用i
指代,将b
用left
指代,将c
用right
指代i
在下标0
处开始,left
定义在i+1
处,right
定义在数组结尾
处
- 循环中
left
和right
移动逻辑:nums[i] + nums[left] + nums[right] > 0
:三数之和偏大,right
下标向左移动,减小三数之和nums[i] + nums[left] + nums[right] < 0
:三数之和偏小,left
下标向右移动,增大三数之和- 直到
left
与right
相遇为止 - 时间复杂度:O(n2)
- 空间复杂度:O(1)
- 去重逻辑
a
的去重:结果中不能有重复的三元组,但三元组内的元素可以重复if (i > 0 && nums[i] == nums[i - 1]) { continue; }
b
与c
的去重:无需单独去重
-
哈希法
- 好难不会
代码
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
//排序
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++)
{
//排序后第一个元素不应大于0
if(nums[i] > 0)
{
return result;
}
if(i > 0 && nums[i] == nums[i - 1])
{
continue;
}
int left = i+1;
int right = nums.length - 1;
while(left < right)
{
int sum = nums[i] + nums[left] + nums[right];
if(sum > 0)
{
right --;
}else if(sum < 0)
{
left ++;
}else
{
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
while(right > left && nums[right] == nums[right - 1]) right--;
while(right > left && nums[left] == nums[left + 1]) left++;
right--;
left++;
}
}
}
return result;
}
}
三、18. 四数之和
题目
思路
-
双指针法
- 先对数组进行排序
- 后在
for
循环中,将a
用k
指代,将b
用i
指代,将c
用left
指代,将d
用right
指代k
在下标0
处开始,i
在下标k+1
处开始,left
定义在i+1
处,right
定义在数组结尾
处
- 此题
target
不为恒定,故剪枝
条件应为nums[i] > target && (nums[i] >=0 || target >= 0)
- 循环中
left
和right
移动逻辑:nums[k] + nums[i] + nums[left] + nums[right] > 0
:三数之和偏大,right
下标向左移动,减小三数之和nums[k] + nums[i] + nums[left] + nums[right] < 0
:三数之和偏小,left
下标向右移动,增大三数之和- 直到
left
与right
相遇为止 - 时间复杂度:O(n3)
- 空间复杂度:O(1)
- 去重逻辑
a b
的去重:结果中不能有重复的四元组,但四元组内的元素可以重复if (i > 0 && nums[i] == nums[i - 1]) { continue; }
c
与d
的去重:无需单独去重
-
哈希法
- 好难不会
代码
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
//排序
Arrays.sort(nums);
//结果集
List<List<Integer>> result = new ArrayList<>();
for(int k = 0; k < nums.length; k++)
{
//剪枝处理
if(nums[k] > target && nums[k] >= 0)
{
break;
}
//对nums[k]去重
if(k > 0 && nums[k] == nums[k - 1])
{
continue;
}
for(int i = k+1; i < nums.length; i++)
{
//第二级剪枝
if(nums[k] + nums[i] > target && nums[k] + nums[i] >= 0)
{
break;
}
//对nums[i]去重
if(i > k + 1 && nums[i] == nums[i - 1])
{
continue;
}
int left = i+1;
int right = nums.length - 1;
while(right > left)
{
long sum = (long) nums[k] + nums[i] + nums[left] + nums[right];
if(sum > target)
{
right--;
}else if(sum < target){
left++;
} else {
result.add(Arrays.asList(nums[k], nums[i], nums[left], nums[right]));
//对nums[left]和nums[right]去重
while(right > left && nums[right] == nums[right - 1]) right--;
while(right > left && nums[left] == nums[left + 1]) left++;
right--;
left++;
}
}
}
}
return result;
}
}