454.四数相加II
在使用map时,要学会函数的使用。在本题中,用到了getOrdefault(key,defaultValue)函数。其中,key表示Map中的键,defaultValue表示当键key不存在时返回的默认值。
函数的由来:在Java 8中,Map接口中引入了一个新的方法getOrDefault(key, defaultValue),用于获取指定key对应的value,若该key不存在,则返回defaultValue。此方法使用方便,代码简洁,常被用于避免NullPointerException异常。
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int res = 0; //统计四数之和为0的组数
Map<Integer,Integer> map = new HashMap<Integer,Integer>(); //创建unordered_map,key为两数之和,value存放次数
//计算nums1和nums2的两数之和,并记录出现的次数
for(int i:nums1)
for(int j:nums2){
int sum = i+j;
map.put(sum,map.getOrDefault(sum,0)+1);
}
//计算nums3和nums4的两数之和,判断其负数是否在map中,若存在,说明四数之和为0
for(int i:nums3)
for(int j:nums4)
res += map.getOrDefault(0-i-j,0);
return res;
}
383. 赎金信
这道题目和242.有效的字母异位词 (opens new window)很像。根据题意,很明显用数组哈希法。
在这道题中,要记住的函数是toCharArray()函数,"toCharArray()"是String类的一个方法,用于将字符串转换为一个字符数组。它返回一个新的字符数组,该数组包含字符串中的所有字符,且顺序与原字符串保持一致。
public boolean canConstruct(String ransomNote, String magazine) {
int[] count = new int[26]; //存放字母出现的次数
//记录ransomNote字母出现次数
for(char i:ransomNote.toCharArray())
count[i-'a']++;
//与ransomNote中出现的字母次数抵消(可以理解为删除了ransomNote中的字母)
for(char j:magazine.toCharArray())
count[j-'a']--;
//ransomNote中的字母是否有未被抵消的
for(int k = 0;k < 26;k++)
if(count[k] > 0)
return false; //若有,则返回false
return true; //若没有,则返回true
}
15. 三数之和
本题与 1. 两数之和 类似,是非常经典的面试题,但是做法不尽相同。
因为本题是不能重复的,所以存在去重的问题。但是本题如果用哈希法的话去重会比较复杂,因此使用双指针法。
不重复的本质:保证枚举的三元组(a,b,c)满足a ≤ b ≤ c,保证了只有(a,b,c)这个顺序会被枚举到。
而且要注意,要先对数组进行排序后,才能进行双指针法。因为本题只要知道元素相加等于0,返回的不是下标,因此可以先进行排序。而且双指针法的前提是数组有序。
本题中,注意下Arrays.sort()函数,是对数组进行排序的。还要注意下List的创建。
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList();
int len = nums.length;
if(nums == null || len < 3) //考虑边界情况
return result;
//选择排序
for(int i = 0;i < len;i++){
int min = i; //这里要注意,是每轮循环时min都要先为i
for(int j = i+1;j < len;j++)
if(nums[min] > nums[j])
min = j;
int temp = nums[min];
nums[min] = nums[i];
nums[i] = temp;
}
// 找出a + b + c = 0
// a = nums[i], b = nums[left], c = nums[right]
for(int i = 0;i < len;i++){
if(nums[i] > 0)
break;
//对a去重,因为 i = 0是第一次出现,肯定不会有重复的,所以要与前面的比较
if(i > 0 && nums[i] == nums[i-1])
continue;
int left = i+1;
int right = len-1;
while(left < right){
int sum = nums[i] + nums[left] + nums[right];
if(sum == 0){
result.add(Arrays.asList(nums[i],nums[left],nums[right]));
//给b和c去重,三元组间去重
while(left < right && nums[left] == nums[left+1]) //去重b
left++;
while(left < right && nums[right] == nums[right-1]) //去重c
right--;
//注意left和right的更新
left++;
right--;
}else if(sum < 0)
left++;
else
right--;
}
}
return result;
}
18. 四数之和
四数之和与三数之和的方法一样,也是排序+双指针法。
四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3) 。
那么一样的道理,五数之和、六数之和等等都采用这种解法。
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<>();
int len = nums.length;
//判断特殊情况,比如为空或者数组长度小于4
if(nums == null || len < 4)
return result;
//选择排序
for(int i = 0;i < len;i++){
int min = i;
for(int j = i+1;j < len;j++)
if(nums[min] > nums[j])
min = j;
int temp = nums[i];
nums[i] = nums[min];
nums[min] = temp;
}
for(int i = 0;i < len;i++){
//比如:数组是[-4, -3, -2, -1],target是-10,不能因为-4 > -10而跳过,所以要价格nums[i]>0
if(nums[i] > 0 && nums[i] > target)
return result;
if(i > 0 && nums[i] == nums[i-1]) //对nums[i]去重
continue;
for(int j = i+1;j < len;j++){
if(j > i+1 && nums[j] == nums[j-1]) //对nums[j]
continue;
int left = j+1;
int right = len-1;
while(left < right){
long sum = (long)nums[i] + nums[j] + nums[left] + nums[right];
if(sum == target){
result.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
//对left去重
while(left < right && nums[left] == nums[left+1])
left++;
//对right去重
while(left < right && nums[right] == nums[right-1])
right--;
//更新right和left的值
right--;
left++;
}else if(sum > target)
right--;
else
left++;
}
}
}
return result;
}