454. 四数相加
题目:力扣
这道题因为没有考虑重复元素问题,因此可以考虑简单一些。
可以将数组分为两组。
A + B + C + D = 0 ==> A+B = 0 -(C+D)
用map储存A+B「key」以及出现频率「value」,然后用C+D查找是否存在A+B - 累加频率
代码实现:
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
HashMap<Integer, Integer> map = new HashMap<>();
for(int i=0; i<nums1.length; i++){
for(int j=0; j<nums2.length; j++){
int sum = nums1[i]+nums2[j];
map.put(sum, map.getOrDefault(sum, 0)+1);
}
}
int result = 0;
for(int i=0; i<nums3.length; i++){
for(int j=0; j<nums4.length; j++){
int target = nums3[i]+nums4[j];
if(map.containsKey(0-target)){
result+= map.get(0-target);
}
}
}
return result;
}
参考资料:代码随想录
383. 赎金信
题目:力扣
解法其实和有效的分母异位词类似,使用hashmap,这里因为明确是使用magazine 来生成ransom,因此需要储存进map的字符以及频率就必须是magazine。
然后遍历ransom,如果有在map中不存在的字符则直接返回false;有存在的字符则将频率-1 ==》代表被使用;如果频率小于0,则返回false。
public boolean canConstruct(String ransomNote, String magazine) {
HashMap<Character, Integer> map = new HashMap<>();
for(int i=0; i<magazine.length(); i++){
map.put(magazine.charAt(i), map.getOrDefault(magazine.charAt(i),0)+1);
}
for(int i=0; i<ransomNote.length(); i++){
if(map.containsKey(ransomNote.charAt(i))){
map.put(ransomNote.charAt(i), map.get(ransomNote.charAt(i))-1);
if(map.get(ransomNote.charAt(i)) < 0) return false;
}else{
return false;
}
}
return true;
}
15. 三数之和
第一反应是先排序;然后使用双指针,固定第一位,然后用left和right指向数组剩下的头和尾;若是和为0则保存数列;若是大于0则right向左移动 - 减小;若是小于0则left向右移动 - 增加。
整体逻辑很简单,但是注意细节:
因为需要返回不重复的list,那么就需要去重。
如何去重?
第一种方案是采用hashset - 这里注意使用Stream进行集合类型转换
public List<List<Integer>> threeSum(int[] nums) {
if(nums == null || nums.length < 3){
return null;
}
Arrays.sort(nums);
HashSet<List<Integer>> set = new HashSet<>();
for(int i=0; i<nums.length-2; i++){
if(nums[i] > 0) break; //剪枝
int left = i+1;
int right = nums.length -1;
int sum = nums[i] + nums[left] + nums[right];
while(left < right){
if(sum == 0){
set.add(Arrays.asList(nums[i], nums[left], nums[right]));
left++;
right--;
}else if(sum > 0){
right --;
}else{
left++;
}
sum = nums[i] + nums[left] + nums[right];
}
}
return set.stream().toList();
// List<List<Integer>> result = new ArrayList<>();
// for(List<Integer> l : set){
// result.add(l);
// }
// return result;
}
第二种方案是通过比较指针移动的数是否相同 - 这里有个细节问题不能讲三元素中存在重复元素的list忽略
也就是这个写法存在问题:
if (nums[i] == nums[i + 1]) { // 去重操作
continue;
}
例如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断 下一个也是-1,那这组数据就pass了。
应该这样写:
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
当前使用 nums[i],我们判断前一位是不是一样的元素,在看 {-1, -1 ,2} 这组数据,当遍历到 第一个 -1 的时候,只要前一位没有-1,那么 {-1, -1 ,2} 这组数据一样可以收录到 结果集里。
具体代码实现:
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
if(nums == null || nums.length < 3){
return result;
}
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++){
//第一个数大于0,那么说明三数和肯定大于0
if(nums[i] > 0){
break;
}
//如何去重
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){
result.add(Arrays.asList(nums[i],nums[left],nums[right]));
//去重
while(left < right && nums[left] == nums[left+1]){
left++;
}
while(left < right && nums[right] == nums[right-1]){
right--;
}
left++;
right--;
}else if(sum < 0){
left++;
}else{
right--;
}
}
}
return result;
}
参考资料:代码随想录
18. 四数之和
逻辑和三数之和是一样的,但是细节上需要注意:
- 剪枝问题 - 不要判断
nums[k] > target
就返回了, 四数之和这道题目 target是任意值。比如:数组是[-4, -3, -2, -1]
,target
是-10
,不能因为-4 > -10
而跳过。但是我们依旧可以去做剪枝,逻辑变成nums[i] > target && (nums[i] >=0 || target >= 0)
就可以了。
public List<List<Integer>> fourSum(int[] nums, int target) {
if(nums == null || nums.length <4) return new ArrayList<>();
Arrays.sort(nums);
HashSet<List<Integer>> set = new HashSet<>();
for(int i=0; i<nums.length -3; i++){
//剪枝操作
if(nums[i] > target && nums[i] > 0) break;
for(int j=i+1; j<nums.length -2; j++){
// if(nums[i] + nums[j] > target) continue;
int left = j+1;
int right = nums.length -1;
int sum = nums[i] + nums[j] + nums[left] +nums[right];
while(left < right){
if(sum == target){
set.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
left++;
right --;
}else if(sum > target){
right --;
}else{
left++;
}
sum = nums[i] + nums[j] + nums[left] +nums[right];
}
}
}
return set.stream().toList();
}
参考资料:代码随想录