哈希表 算法跟学Day7【代码随想录】
大纲
● 454.四数相加II
● 383. 赎金信
● 15. 三数之和
● 18. 四数之和
● 总结
leetcode 454
四数相加II
思路
- 因为是四个数组 必然要逐个遍历 但一次遍历消耗的时间过久 拆分成两个两个进行遍历
- 先把前两个数组遍历,对于两数组得到的和进行保存
- 再遍历后两个数组,每次拿出之前得到的和查看当前是否有满足条件的解
细节
- 注意两数组之和可能会有值重复 故而用map分别保存其和以及出现的次数
代码
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int res = 0;
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums1.length; i++)
for (int j = 0; j < nums2.length; j++)
map.put(nums1[i] + nums2[j], map.getOrDefault(nums1[i]+nums2[j], 0) + 1);
for (int i = 0; i < nums3.length; i++)
for (int j = 0; j < nums4.length; j++)
res += map.getOrDefault(0 - nums3[i] - nums4[j], 0);
return res;
}
}
复杂度
- 时间
O(n^2)
- 空间
O(n^2)
最坏情况下nums1和nums2之和各不相同
leetcode 383
赎金信
思路
- 空间换时间的方法 采用一个哈希表记录
magazine
中出现字符 - 遍历待检测字符串
ransomNote
并将哈希表出现元素自减 - 检查哈希表中元素是否超减
细节
- 注意题目中仅包含小写字母 故创建长度为26的整型自动映射
代码
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int[] tab = new int[26];
for (int i = 0; i < magazine.length(); i++) tab[magazine.charAt(i) - 'a']++;
for (int i = 0; i < ransomNote.length(); i++) tab[ransomNote.charAt(i) - 'a']--;
for (int i : tab) if (i < 0) return false;
return true;
}
}
复杂度
- 时间 O(n)
- 空间 O(1)
leetcode 15
三数之和
思路
- 遇事不决先排序 步步逼近双指针
- 排序后再指针对撞
细节
- 注意剪枝和重复值的跳过
代码
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 - 2; i++) {
if (nums[i] > 0) break;
if (i > 0 && nums[i] == nums[i - 1]) continue;
int j = i + 1, k = nums.length - 1;
while (j < k) {
int s = nums[i] + nums[j] + nums[k];
if (s == 0) {
res.add(new ArrayList<>(Arrays.asList(nums[i], nums[j++], nums[k--])));
while (j < k && nums[j] == nums[j - 1]) j++;
while (j < k && nums[k] == nums[k + 1]) k--;
}
else if (s > 0) {
k--;
while (j < k && nums[k] == nums[k + 1]) k--;
}
else {
j++;
while (j > k && nums[j] == nums[j - 1]) j++;
}
}
}
return res;
}
}
复杂度
- 时间 O(n^2)
- 空间 O(1)
leetcode 18
四数之和
思路
- 遇事不决先排序 步步逼近双指针
- 固定两数 剩余两数采用双指针对撞
细节
- 注意剪枝细节 重复值的跳过
- break 和 continue的灵活使用
代码
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i < nums.length - 3; i++) {
if (nums[i] > target && nums[i] > 0) break;
if (i > 0 && nums[i] == nums[i - 1]) continue;
for (int j = i + 1; j < nums.length - 2; j++) {
if (nums[i] + nums[j] > target && nums[i] + nums[j] > 0) break;
if (j > i + 1 && nums[j] == nums[j - 1]) continue;
int l = j + 1, r = nums.length - 1;
while (l < r) {
int s = nums[i] + nums[j] + nums[l] + nums[r];
if (s == target) {
res.add(new ArrayList(Arrays.asList(nums[i], nums[j], nums[l++], nums[r--])));
while (l < r && nums[l] == nums[l - 1]) l++;
while (l < r && nums[r] == nums[r + 1]) r--;
} else if (s > target) {
r--;
while (l < r && nums[r] == nums[r + 1]) r--;
} else {
l++;
while (l < r && nums[l] == nums[l - 1]) l++;
}
}
}
}
return res;
}
}
复杂度
- 时间 O(n^3)
- 空间 O(1)