代码随想录算法训练营第七天| 454.四数相加II、383.赎金信、15.三数之和、18.四数之和

系列文章目录


目录

  • 系列文章目录
  • 454.四数相加II
    • 使用HashMap法
  • 383.赎金信
    • 哈希解法(数组)
  • 15.三数之和
    • 双指针法
  • 18.四数之和
    • 双指针法


454.四数相加II

题解:该题和1.两数之和的方法是一样的,这个题的难点在于keyvalue分别是什么。key是相加的值,value是这个值出现的次数。怎么计算这个值出现的次数又是另一个难点了。需要用到Map接口的getDoDefault方法,getOrDefault(key,0):找到key对应的value,如果key不存在,就返回0

使用HashMap法

import java.util.HashMap;//leetcode submit region begin(Prohibit modification and deletion)
class Solution {public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {HashMap<Integer, Integer> map = new HashMap<>();//统计两个数组中的元素之和,同时统计出现的次数,放入mapfor (int i = 0; i < nums1.length; i++) {for (int j = 0; j < nums2.length; j++) {int sum = nums1[i] + nums2[j];
/*                if (!map.containsKey(sum)) {//如果没加过该keymap.put(sum, 1);} else {//加过该key则替换value值,将value+1map.put(sum, map.get(sum) + 1);}*/map.put(sum, map.getOrDefault(sum, 0) + 1);}}//统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数int count = 0;for (int i = 0; i < nums3.length; i++) {for (int j = 0; j < nums4.length; j++) {int sum = nums3[i] + nums4[j];if (map.containsKey(-sum)) {count += map.get(-sum);}}}return count;}
}
//leetcode submit region end(Prohibit modification and deletion)

注:统计key值出现的次数,有两种方法改变value值。
①利用只能加入一个key,若重复加入key则替换value来实现。

if (!map.containsKey(sum)) {//如果没加过该keymap.put(sum, 1);
} else {//加过该key则替换value值,将value+1map.put(sum, map.get(sum) + 1);
}

②通过getDoDefault方法实现。(快一些)

map.put(sum, getOrDefault(sum, 0) + 1);

383.赎金信

题解:因为两个字符串都是由小写字母组成的,所以说数据的范围是确定的,那么就可以用数组结构。用每个字符对应其数组下标,数组值就是每个字母出现的次数,最后遍历另一个字符串,需要的字母就-1,最后看结果数组的是否有负值,有负值说明字母是不够的,返回false

哈希解法(数组)

class Solution {public boolean canConstruct(String ransomNote, String magazine) {//剪枝if (ransomNote.length() > magazine.length()) {return false;}int[] record = new int[26];//遍历第一个字符串,通过record数据记录 magazine里各个字符出现次数for (int i = 0; i < magazine.length(); i++) {record[magazine.charAt(i) - 'a']++;}/*//遍历ransomNote+判断数组值是否为负for (int i = 0; i < ransomNote.length(); i++) {if(--record[ransomNote.charAt(i)-'a']<0){//只要有一个数组值为负数,就返回falsereturn false;}}*/// 遍历ransomNote,在record里对应的字符个数做--操作for (int i = 0; i < ransomNote.length(); i++) {record[ransomNote.charAt(i) - 'a']--;}//判断数组值是否为负for (int i = 0; i < record.length/*ransomNote.length()*/; i++) {// 如果小于零说明ransomNote里出现的字符,magazine没有if (record[i]/*record[ransomNote.charAt(i) - 'a']*/ < 0) {return false;}}return true;}
}

在这里插入图片描述
注:从字符到数组下标的映射比较耗时间,故最后判断数组值是否为负时直接遍历record数组即可,不要再映射了。


15.三数之和

题解:在同一个数组中找一个三个数(这三个数的下标不同),相加和为0这个三元组不能重复,但里面的元素可以重复。如果数组是 [0,0,1,1,-1,-1,-2],则 [0,1,-1][0,1,-1]不合法,但是[1,1,-2]合法。

双指针法

import java.util.ArrayList;
import java.util.Arrays;//leetcode submit region begin(Prohibit modification and deletion)
class Solution {public List<List<Integer>> threeSum(int[] nums) {//注意题目中方法要求返回<List<Integer>>泛型List<List<Integer>> result = new ArrayList<>();Arrays.sort(nums);// 找出a + b + c = 0// a = nums[i], b = nums[left], c = nums[right]for (int i = 0; i < nums.length; i++) {// 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了if (nums[i] > 0) {return result;}//去重a//一定要有i>0条件,因若i=0,此时nums[i-1]=-1,数组越界异常if (i > 0 && nums[i] == nums[i - 1]) {continue;//跳过本次循环进入下次循环,不能i++因还会执行本次循环}int left = i + 1;int right = nums.length - 1;while (left < right) {//剪枝:排序之后如果最后一个元素小于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了if(nums[right]<0){System.out.println("1111");return result;}if (nums[i] + nums[left] + nums[right] > 0) {right--;} else if (nums[i] + nums[left] + nums[right] < 0) {left++;} else {result.add(Arrays.asList(nums[i], nums[left], nums[right]));// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重while (left < right && nums[right] == nums[right - 1]) right--;while (left < right && nums[left] == nums[left + 1]) left++;right--;left++;}}}return result;}
}
//leetcode submit region end(Prohibit modification and deletion)

注:①题目中方法要求返回<List<Integer>>泛型,故在创建ArrayList时要注意。
Arrays类里面包含了一系列静态方法,用于管理或操作数组,故调用sort方法对数组进行自然排序。
在这里插入图片描述
③剪枝操作:排序后,在遍历数组时,

  1. 如果三个数中最小的数即a=nums[i]已经>0,则已不可能三个数之和为0,故可直接退出。

           for (int i = 0; i < nums.length; i++) {//剪枝:排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了if (nums[i] > 0) {return result;}...}
    
  2. 如果最后一个元素c=nums[right]已经<0,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了。

           while (left < right) {//剪枝:排序之后如果最后一个元素小于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了if(nums[right]<0){System.out.println("1111");return result;}...}
    

④对a进行去重时,一定要有i>0条件,因若i=0,此时nums[i-1]=-1,数组越界异常。
在这里插入图片描述
⑤对b=nums[left],c=nums[right]的去重操作。

  1. 放在找到三元组之前,[0,0,0] 的情况,可能直接导致 right<=left 直接退出循环了,此时还没加入一个三元组,从而漏掉了[0,0,0]这种三元组。

    while (left < right) {// 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组/*while (right > left && nums[right] == nums[right - 1]) right--;while (right > left && nums[left] == nums[left + 1]) left++;*/if (...)...
    }
    
  2. 去重逻辑应该放在找到一个三元组之后,对b、c去重。此时判断right是否与左边元素相等,若相等则跳过该左边的元素;判断left是否与右边元素相等,若相等则跳过该右边的元素。最后两行的 right--; left++;不管b、c有没有重复都得有,其意味着三元组此时已经=0,不论单独移动哪一边,都不会再有=0的情况了,故必须得同时移动。
    在这里插入图片描述

  3. 很多同学写本题的时候,去重的逻辑多加了 对rightleft 的去重:nums[right] == nums[right + 1]其实比较的还是当前right指针即right + 1与左边元素right,因前面有个 right--;,已经将right指针左移了,此时指向的是左边元素。nums[left] == nums[left - 1]同理。

    while (right > left) {if (nums[i] + nums[left] + nums[right] > 0) {right--;// 去重 rightwhile (left < right && nums[right] == nums[right + 1]) right--;} else if (nums[i] + nums[left] + nums[right] < 0) {left++;// 去重 leftwhile (left < right && nums[left] == nums[left - 1]) left++;} else {}
    }
    

    在这里插入图片描述
    因此时是在!=0的条件下去重的,不论是right还是left,即使去重了,去重后得到的rightleft指针还是指向三元组!=0的元素。

在这里插入图片描述


18.四数之和

双指针法

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/536095.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】-Linux下的软件商店yum工具介绍(linux和windows互传文件仅仅一个拖拽搞定!!!!)

目录 1.Linux 软件包管理器yum 1.1快速认识yum 1.2 yumz下载方式&#xff08;如何使用yum进行下载&#xff0c;注意下载一定要是root用户或者白名单用户&#xff08;可提权&#xff09;&#xff09; 1.2.1下载小工具rzsz 1.2.2 rzsz使用 1.2.2查看软件包 1.3软件的卸载 2.yum生…

C语言 指针(5) 数组和指针题解析

目录 前言 一、sizeof和strlen 二、数组和指针笔试题解析 2.1、一维数组 2.2 字符数组 2.3 二维数组 三、指针运算笔试题解析 总结 前言 今天我们继续来了解指针&#xff0c;讲一下sizeof和strlen的区别&#xff0c;数组和指针题解析 一、sizeof和strlen 我们之前在讲…

跨境电子商务支付与结算的支撑系统

​1、跨境电子商务支付与结算的核心系统。 核心系统是用户执行跨境电子商务支付的核心模块&#xff0c;包括以下具体流程。 ​ ​①用户从跨境电子商务支付应用启动跨境电子商务支付流程。 ②跨境电子商务支付应用根据应用和用户选择的支付工具&#xff0c;来调用对应的支付产…

与鲸同行,智领未来!和鲸科技高校市场渠道合作伙伴正式开启招募

AI 浪潮来袭&#xff0c;技术日新月异&#xff0c;校企合作已成为高校培养符合产业需求的应用型人才、加速科研创新与成果转化的关键途径。从单一应用到多元化布局&#xff0c;各企业更需要技术领先、战略协同的领域伙伴协力共进。 和鲸科技以“协同平台实践社区竞赛”三位一体…

【数据结构】Set的使用

文章目录 一、Set的使用1.Set的常用方法&#xff1a;1.boolean add(E e)2.void clear()3.boolean contains(Object o)4.boolean remove(Object o)5.int size()6.boolean isEmpty()7.Object[] toArray()8.boolean containsAll(Collection<?> c)9.boolean addAll(Collecti…

C语言函数—关于静态库

具体的函数声明和定义请参考上一篇文章 如果我们成为了库的开发者&#xff0c;要卖给别人C语言库&#xff0c;该怎么办呢&#xff1f; A不会写减法&#xff0c;想找你买一个函数 但是&#xff0c;他给的太少了&#xff0c;你不想把源码卖给他 那怎么办呢&#xff1f; 首先&…

Android SDK 开发 云托管

开发SDK,拒绝重复造轮子。 本文陈述两种SDK开发方式&#xff0c;第一种AAR方式&#xff1b;第二种远程依赖方式。 具体步骤分为&#xff1a; 一、如何开发SDK&#xff1f; 二、如何打包AAR&#xff1f; 三、如何打包AAR&#xff1f; 四、如何进行SDK远程托管&#xff1f; 五、如…

【JavaScript】数据类型转换 ① ( 隐式转换 和 显式转换 | 常用的 数据类型转换 | 转为 字符串类型 方法 )

文章目录 一、 JavaScript 数据类型转换1、数据类型转换2、隐式转换 和 显式转换3、常用的 数据类型转换4、转为 字符串类型 方法 一、 JavaScript 数据类型转换 1、数据类型转换 在 网页端 使用 HTML 表单 和 浏览器输入框 prompt 函数 , 接收的数据 是 字符串类型 变量 , 该…

java拷贝数组

package com.mohuanan.exercise;public class Exercise {public static void main(String[] args) {int[] arr {1, 2, 3, 4, 5, 6, 7, 8, 8}; //格式化快捷键 CTRL 加 Alt 加 L键// F1截图 F3贴图//调用 copyOfRangeint[] ints copyOfRange(arr, 3, 7);for (int i 0; i &l…

【C++】string类初步介绍

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 为什么学习string类1.1 C语言中的字符串1.2 推荐学习网站 2. 标准库中的string类2.1 string类2.2 string类的常用接口说明2.2.1 constructor2.2.2 遍历string2.2.2.1 下标加[]遍历2.2.2.2 迭代器&#xff08;itera…

前端跨页面通信的几种方式---同源

参考链接 1、LocalStorage:当 LocalStorage 变化时&#xff0c;会触发storage事件。利用这个特性&#xff0c;我们可以在发送消息时&#xff0c;把消息写入到某个 LocalStorage 中&#xff1b;然后在各个页面内&#xff0c;通过监听storage事件即可收到通知。 2、BroadCast C…

快速排序 刷题笔记

思路 分治双指针 在每个区间选定一个基准目标 两个指针从数组的两边向中间推进 使用 while循环判断 do {i;}while(q[i]<x); do{j--;}while(q[j]>x); 每次这样做完就会找到q[i]>x,,,,q[j]小于x 此时我们交换 q[i] ,q[j]于是小于x的数分到了小于x的一侧 大…