LeetCode 454.四数相加II
想法:看到本题的想法是将四数之和转化为两数之和进行计算,这样就能将四个循环降到两个循环,虽然有想法,但却不知道如何下手,在思考是否有更简单的解法(不敢相信是用两个for循环遍历),可能还是不够相信自己的判断,后来看了讲解文章,发现和自己的思路是一致的。代码如下:
import java.util.Map;
import java.util.HashMap;
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer, Integer> record = new HashMap<Integer, Integer>();
int count = 0;
for(int i : nums1){
for(int j : nums2){
record.put(i + j, record.getOrDefault(i + j, 0) + 1);
}
}
for(int k : nums3){
for(int l : nums4){
count += record.getOrDefault(-k-l, 0);
}
}
return count;
}
}
也是从卡尔的文章中学到的map的赋值处理,之前一直加if判断是否为null,原来java的map对象是有getOrDefault函数来进行初始的,又是学到知识的一天。
LeetCode 383.赎金信
想法:分别用两个Map对象来保存两个字符串中的字符及相应的出现次数,由于题目要求使用magazine中的字符来组成ransomNote,所以ransomNote中各字符的数量应小于等于magazine中该字符出现的次数。
代码如下:
import java.util.Map;
import java.util.HashMap;
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
Map<String, Integer> record1 = new HashMap<String, Integer>();
Map<String, Integer> record2 = new HashMap<String, Integer>();
String[] s1 = ransomNote.split("");
String[] s2 = magazine.split("");
for(int i=0;i<s1.length; i++){
record1.put(s1[i], record1.getOrDefault(s1[i], 0) + 1);
}
for(int i=0;i<s2.length; i++){
record2.put(s2[i], record2.getOrDefault(s2[i], 0) + 1);
}
for(String key: record1.keySet()){
// ransomNote中字符出现次数应小于等于magazine中改字符出现的次数
if(record1.get(key) > record2.getOrDefault(key, 0)) return false;
}
return true;
}
}
看了讲解文章,发现自己并没有关注到问题的关键点,即字符串中只有小写字母,对于这种取值范围有限制的情况下,一般用数组的耗时比map要小很多,所以这题可以采用和LeetCode 242.有效的字母异位词相同的解法,用一个长度为26的int数组来记录字符的出现次数,最后判断如果有负数的情况即说明magazine中的字母不足以组成字符串ransomNote,代码如下:
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int[] record = new int[26];
for(int i=0;i<magazine.length(); i++){
record[magazine.charAt(i) - 'a']++;
}
for(int i=0;i<ransomNote.length(); i++){
record[ransomNote.charAt(i) - 'a']--;
}
for(int count: record){
// ransomNote中字符出现次数应小于等于magazine中改字符出现的次数
if(count < 0) return false;
}
return true;
}
}
LeetCode 15.三数之和
想法:想着类似于四数之和,用Map对象以前两数之和为key,前两数组成的数组为value进行遍历,然后再找第三个数。后来想想这样做的话对同一个数组遍历三次是否有必要,滑动窗口是否可以解决这个问题?如果用双指针的话,头和尾指针要怎么更新呢?最后还是没想到怎么做,看了讲解视频后大概知道要怎么做了。
思路:关键点在于要先对数组进行升序排序,这样后续找数以及去重会好处理许多。大体思路是用一个for循环来便利数组nums,其中i是第一个数的下标,left为第二个数下标,初始为i+1,right为第三个数下标,初始为nums.length-1(这里是双指针的精髓所在),判断三数之和是否等于0,若大于0,则rigth--,若小于0,则left++,依次遍历,该有一些去重的细节在代码中有体现:
import java.util.Arrays;
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums); // 对数组进行升序排序
List<List<Integer>> result = new ArrayList<>();
for(int i=0; i<nums.length; i++){
if(nums[i] > 0) break;
if(i>0 && nums[i] == nums[i-1]) continue; // nums[i]去重
int left = i+1;
int right = nums.length - 1;
while(left < right){
if(nums[i] + nums[left] + nums[right] > 0){
right--;
}else if(nums[i] + nums[left] + nums[right] < 0){
left++;
}else{
// List<Integer> temp = new ArrayList<>();
// temp.add(nums[i]);
// temp.add(nums[left]);
// temp.add(nums[right]);
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
while(left < right && nums[left] == nums[left + 1]){ // nums[left]去重
left++;
}
while(left < right && nums[right] == nums[right-1]){ // nums[right]去重
right--;
}
right--;
left++;
}
}
}
return result;
}
}
其实在看视频以及文章时,我一直有个疑问,i的取值范围是[0, nums.length-1],而在for循环中,left=i+1,当i=nums.length-1时,数组不会越界吗?后来想想如果真到此时,right也等于nums.length-1,left大于right,是进不去循环的,也就不存在越界的情况,所以这里不需要做特殊的越界处理。
LeetCode 18.四数之和
与上题三数之和的解法类似,在双指针的基础上套两层for循环即可。本人在代码AC的过程中踩的坑:1.第二个循环j的去重,之前以为这里不需要去重了,但是提交说测试不通过,所以参考第一层循环i加上了去重的条件判断;2.缺少数组和target的边界剪枝处理,因为对数组进行了排序,当num[i]>0 && nums[i]>target时,说明第一个元素及后面的元素再怎么求和都会比target大,这个需要特殊处理一下。代码如下:
import java.util.Arrays;
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for(int i=0;i<nums.length; i++){
if(nums[i] > 0 && nums[i] > target) break;
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(left < right){
if(nums[i] + nums[j] + nums[left] + nums[right] > target){
right--;
}else if(nums[i] + nums[j] + nums[left] + nums[right] < target){
left++;
}else{
result.add(Arrays.asList(nums[i] , nums[j] , nums[left] , nums[right]));
while(left < right && nums[left] == nums[left + 1]) left++;
while(left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
}
}
}
}
return result;
}
}
这篇博客与上篇博客隔了一周时间,这周有所懈怠,已经落后进度5天了,尽力追赶吧。