第454题.四数相加II
题目链接:454. 四数相加 II - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:学透哈希表,map使用有技巧!LeetCode:454.四数相加II_哔哩哔哩_bilibili
解题思路:
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer, Integer> plus = new HashMap<>();
for (int i : nums1) {
for (int j : nums2) {
int sum = i + j;
//遍历两个数组,记录两个数组每两个元素的和
plus.put(sum, plus.getOrDefault(sum, 0) + 1);
//将和放在hashmap中,如果key已存在,则value加一,不存在则使其value等于1
}
}
int count = 0;
//统计四数相加为0出现的次数
for (int i : nums3) {
for (int j : nums4) {
count += plus.getOrDefault(0 - i - j, 0);
//如果存在0-(c+d)=a+b,则count增加value
}
}
return count;
}
}
时间复杂度:O(n^2)
该题总结:这道题需要知道用那种哈希结构,在了解到用hashmap之后思路自然而然地也就出来了。
383. 赎金信
题目链接:383. 赎金信 - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
解题思路:
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
if (ransomNote.length() > magazine.length()) {
return false;
}
//ransomNote更长的话就不可能被magazine构成
int[] record = new int[26];
//创建用于记录ransomNote中每个字母出现次数的数组
for (int i = 0; i < ransomNote.length(); i++ ) {
record[ransomNote.charAt(i) - 'a']++;
}
//记录ransomNote中每个字母出现次数
for (int i = 0; i < magazine.length(); i++) {
record[magazine.charAt(i) - 'a']--;
}
//用减法计算一遍magazine中每个字母出现次数
for (int i : record) {
if (i > 0) {
return false;
}
}
//确定record中每个元素是否都小于零,未出现大于零的元素则说明ransomSize可由magezine组成
return true;
}
}
时间复杂度:O(n)
该题总结:这道题就很简单了,在做过了之前的242. 有效的字母异位词 - 力扣(LeetCode)这道题后,看一眼代码随想录上的思路,这道赎金信就没什么难度了,和字母异位词可以说是一模一样的解法。
第15题. 三数之和
题目链接:15. 三数之和 - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:梦破碎的地方!| LeetCode:15.三数之和_哔哩哔哩_bilibili
解题思路:
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++) {
if (nums[i] > 0) {
return result;
}
//排序后nums[i]如果大于0则无法凑成三元组
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
//当前的nums[i]如果和nums[i-1]相同的话直接进入下一个循环来去重
int left = i + 1;
int right = nums.length - 1;
//初始化双指针
while (right > left) {
int sum = nums[i] + nums[left] + nums[right];
//计算三数之和!!!!要放在循环中!!!!
if (sum > 0) {
right--;
//三数之和大于0,右指针左移
} else if (sum < 0) {
left++;
//三数之和小于0,左指针右移
} else {
result.add(Arrays.asList(nums[i],nums[left],nums[right]));
//三数之和为0,添加到result列表中
while (right > left && nums[left] == nums[left + 1]) {
left++;
}
//为left所指的元素去重
while (right > left && nums[right] == nums[right - 1]) {
right--;
}
//为right所指的元素去重
left++;
right--;
//三数之和为0时,双指针收缩
}
}
}
return result;
}
}
时间复杂度:O(n^2)
该题总结:这道题就开始上难度了,不仅仅是一开始的思路想不到,而且其中的剪枝操作和去重操作更是需要耗费精力去想。
第18题. 四数之和
题目链接:18. 四数之和 - 力扣(LeetCode)
文档讲解:代码随想录 (programmercarl.com)
视频讲解:难在去重和剪枝!| LeetCode:18. 四数之和_哔哩哔哩_bilibili
解题思路:
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<>();
if (nums.length < 4) {
return result;
}
//防止数组长度小于4
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (nums[i] > target && target > 0) {
return result;
}
//一级剪枝
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
//一级去重
}
for (int j = i + 1; j < nums.length; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
//二级去重
int left = j + 1;
int right = nums.length - 1;
//初始化左右指针
while (right > left) {
//从两边向中间遍历数组
long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
//初始化总和方便判断总和与目标值的关系
if (sum > target) {
right--;
//总和大于目标值,右指针左移
} else if (sum < target) {
left++;
//总和小于目标值,左指针右移
} else {
result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
//总和等于目标值,将四个数作为一个整体添加到列表中
while (right > left && nums[left] == nums[left + 1]) {
//这里要先检查right是否大于left
left++;
}
//左值去重
while (right > left && nums[right] == nums[right - 1]) {
//这里要先检查right是否大于left
right--;
}
//右值去重
left++;
right--;
//找到目标值时两指针向中间收缩
}
}
}
}
return result;
}
}
specially:这段代码我认为是有问题的,我不知道为什么他能过,本来这段代码在内层for循环里面是有二级剪枝操作的,但一旦做了二级剪枝就有用例过不去,我也想不明白,求助算法训练营的大佬们了已经,有答案会在评论区更新。
时间复杂度:O(n^3)
该题总结:今日重磅题目,可以说是我这7天以来做过的最重量级的题目了,完全难于设计链表,那道题只是题量大,而这道题难点在于各种细枝末节的地方,一级剪枝,一级去重,二级剪枝,二级去重,左右指针去重,一旦有一个地方的循环/判断条件没写对,整个代码就要报错,做这道题可真是费了老大的劲了,可惜最后还有个问题没弄明白,继续加油吧。