【算法训练营Day06】哈希表part2

news2025/6/6 18:22:22

文章目录

  • 四数相加
  • 赎金信
  • 三数之和
  • 四数之和

四数相加

题目链接:454. 四数相加 II

这个题注意它只需要给出次数,而不是元组。所以我们可以分治。将前两个数组的加和情况使用map存储起来,再将后两个数组的加和情况使用map存储起来,key存和,value存出现的次数。得到两个map之后,我们遍历其中一个map, 看另一个map中是否有和为0的情况,有就相加value,最后接可以得出答案。

在这种思路的基础上,我们可以继续优化代码,例如我们在统计后两个数组的同时,就已经可以将需要的和在前两个数组的map中找出来,然后把次数进行相加。

代码如下:

class Solution {
    
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer,Integer> records = new HashMap<>();
        for(int i = 0;i < nums1.length;i++) {
            for(int j = 0;j < nums2.length;j++) {
                records.put(nums1[i] + nums2[j],records.getOrDefault(nums1[i] + nums2[j],0) + 1);
            }
        }

        int result = 0;

        for(int i = 0;i < nums3.length;i++) {
            for(int j = 0;j < nums4.length;j++) {
                Integer count = records.get(0 - nums3[i] - nums4[j]);
                if(count != null) result += count;
            }
        }

        return result;
    }
}

赎金信

题目链接:383. 赎金信

二十六个字母,计数,第一反应就要想到数组。解题思路如下:

  • 用数组统计第一个单词的个数
  • 然后遍历第二个单词,在数组中相应位置进行减法运算
  • 遍历数组
    • 如果存在大于0的数返回false
    • 不存在则返回true
class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int[] records = new int[26];
        for (int i = 0; i < ransomNote.length(); i++) records[ransomNote.charAt(i) - 'a']++;
        for (int i = 0; i < magazine.length(); i++) records[magazine.charAt(i) - 'a']--;
        for (int i = 0; i < records.length; i++) if(records[i] > 0 ) return false;
        return true;
    }
}

三数之和

题目链接:15. 三数之和

解题思路:

看到这一题我们肯定会不自觉地拿它和两数之和进行比较,我们是否能借助两数之和的思想来完成这一题?首先我们回顾一下两数之和的思想。在两数之和中,我们是遍历数组,每遍历一个元素,就看target - 该元素 是否已经出现过(也就是是否在hash表中),如果在直接返回,如果不在就把这个元素添加到hash表中,代表该元素出现过,为后面的元素服务。

在三数相加中,我们尝试沿用这种思路(先不直接到位,后面还会添加新逻辑):

  • 使用双层for循环遍历数组,外层循环相当于固定一个数,内层for循环沿用两数相加的逻辑
  • 初始化一个hashset,用来存已经存在的数,外层循环的以固定值不需要存
  • 内存循环遍历数组,寻找0 - nums[head]- nums[end] 是否存在于hashset中
  • 如果存在那么该数组添加到答案列表中
  • 如果不存在继续遍历
  • 外层循环每完成一次清空set
  • 最后返回答案集合

代码如下:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
	    List<List<Integer>> result = new ArrayList<>();
        HashSet<Integer> set = new HashSet<>();
	    for (int i = 0; i < nums.length; i++) {
		    for (int j = i + 1; j < nums.length; j++) {
			    int need = -nums[i] - nums[j];
			    if (set.contains(need)) {
				    result.add(Arrays.asList(nums[i], nums[j], need));
			    } else {
				    set.add(nums[j]);
			    }
		    }
            set.clear();
	    }
	    return result;
    }
}

这个代码完成了基本的功能但是还差本题的一个重点那就是去重。

就比如题目的这个用例:[-1,0,1,2,-1,-4]
如下两种情况就会重复:

  • i指向-1,j指向1,set里有0,这组会返回
  • i指向0,j指向-1,set里有1,这组也会返回

我们可以尝试排序解决这个问题:

排序之后还是这个用例就变为:[-4,-1,-1,0,1,2]

外层循环在固定到两个-1的时候肯定会发生重复,所以我们可以添加一个条件,外层循环固定的数字和上一次相同时直接跳过:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
	    List<List<Integer>> result = new ArrayList<>();
        HashSet<Integer> set = new HashSet<>();
	    for (int i = 0; i < nums.length; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) {
			    continue;
		    }
		    for (int j = i + 1; j < nums.length; j++) {
			    int need = -nums[i] - nums[j];
			    if (set.contains(need)) {
				    result.add(Arrays.asList(nums[i], nums[j], need));
			    } else {
				    set.add(nums[j]);
			    }
		    }
            set.clear();
	    }
	    return result;
    }
}

改完之后发现这个测试用例过不了:
在这里插入图片描述

原因就是当内层的两数相加满足之后,内层的下一次循环还是相同的数,那么相当于把这一组答案又加了一遍,那么我们针对这个情况进行改进:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
	    List<List<Integer>> result = new ArrayList<>();
        HashSet<Integer> set = new HashSet<>();
	    for (int i = 0; i < nums.length; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) {
			    continue;
		    }
            Integer flag = null;
		    for (int j = i + 1; j < nums.length; j++) {
                if(Integer.valueOf(nums[j]).equals(flag)) continue;
                flag = null;
			    int need = -nums[i] - nums[j];
			    if (set.contains(need)) {
				    result.add(Arrays.asList(nums[i], nums[j], need));
                    flag = nums[j];
			    } else {
				    set.add(nums[j]);
			    }
		    }
            set.clear();
	    }
	    return result;
    }
}

我们最后总结一下这道题:
这道题在沿用了我们前面两数之和的思想之后,会存在一个去重问题:

  • 外层重复:也就是当外层循环固定的数字和上一次相同时此次循环直接跳过
  • 内层重复:也就是当内层的两数相加满足之后,内层的下一次循环还是相同的数。这个时候我们可以在每次三数之和满足条件之后,将内层此次的值记录一下,相邻的下一次循环与此次的值一样就跳过此次内循环。

当然此题也可以使用双指针法来做,逻辑上更为简单,代码在此处不多做赘述。

四数之和

题目链接:18. 四数之和

这题使用双指针法进行解题:

  • 将数组进行排序
  • 首先使用两层的嵌套循环,固定两个数
  • 然后再使用双指针left、right确定最后两个数
  • 将四个数字相加
    • 如果大于目标,right指针左移
    • 如果小于目标,left指针右移
    • 如果达到目标,left指针右移,right指针左移
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
	    List<List<Integer>> result = new ArrayList<>();
        for(int i = 0;i < nums.length;i++) {
            for(int j = i + 1;j < nums.length;j++) {
                int left = j + 1;
                int right = nums.length - 1;
                while(left < right && left < nums.length) {
                    int sum = 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--]));
                    }
                }
                
            }
        }
        return result;
    }
}

接下来进行去重:

在这里插入图片描述

发生这种情况就是因为外层的双层循环固定的两个数字重复,我们添加去重的代码:

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
	    List<List<Integer>> result = new ArrayList<>();
        for(int i = 0;i < nums.length;i++) {
            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 && left < nums.length) {
                    int sum = 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--]));
                    }
                }
            }
        }
        return result;
    }
}

这个去重的逻辑就是:

  • if (i > 0 && nums[i] == nums[i - 1]) continue; 因为数组是按大小排序的,如果第一个固定的数不变,那么其他三个数不管怎么样,都只会与target相等(这个情况已经存在需要去重)或者比target大,所以这个循环可以直接跳过
  • if (j > i + 1 && nums[j] == nums[j - 1]) continue;逻辑类似

执行之后有一个测试样例还是没过:
在这里插入图片描述
造成这个情况的原因是因为,左右指针同时内缩的时候如果元素不变也会发生重复,我们继续往里面添加去重逻辑:

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
	    List<List<Integer>> result = new ArrayList<>();
        for(int i = 0;i < nums.length;i++) {
            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 && left < nums.length) {
                    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[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;
                        left++;
                        right--;
                    }
                }
            }
        }
        return result;
    }
}

注意:测试用例中有一个例子考察的是四数相加的范围超出了int的最大表达值,所以四数相加的和sum要使用long来存储在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Word双栏英文论文排版攻略

word写双栏英文论文的注意事项 排版首先改字体添加连字符还没完呢有时候设置了两端对齐会出现这样的情况&#xff1a; 公式文献 等我下学期有时间了&#xff0c;一定要学习Latex啊&#xff0c;word写英文论文&#xff0c;不论是排版还是公式都很麻烦的&#xff0c;而Latex一键就…

乡村三维建模 | 江苏农田无人机建模案例

测绘是农田建设的基础工作&#xff0c;测绘的质量和效率直接影响农田建设的进度和成果。传统的人工测量、地面测量等测绘手段&#xff0c;存在效率低、精度差、受环境影响大、成本高等缺点&#xff0c;难以满足高标准农田建设的要求。而无人机倾斜摄影技术具有高效、精确、灵活…

2025 5 月 学习笔记

计算高斯半径&#xff0c;用于生成高斯热图 这个的意义是什么 有什么作用&#xff1f; 14 核心意义&#xff1a;平衡定位精度与检测鲁棒性 在基于热图的目标检测方法&#xff08;如CenterNet、CornerNet等&#xff09;中&#xff0c;计算高斯半径的核心意义在于​​在精确…

SpringBoot(七) --- Redis基础

目录 前言 一、Redis入门 二、Redis常用数据类型 三、Redis常用命令 1. 字符串操作命令 2. 哈希操作命令 3. 列表操作命令 4. 集合操作命令 5. 有序集合操作命令 6.通用命令 四、在Java中操作Redis 前言 Redis是一个基于内存的key-value结构数据库&#xff0c;有以下…

从OSI到TCP/IP:网络协议的演变与作用

个人主页&#xff1a;chian-ocean 文章专栏-NET 从OSI到TCP/IP&#xff1a;网络协议的演变与作用 个人主页&#xff1a;chian-ocean文章专栏-NET 前言网络发展LANWAN 协议举个例子&#xff1a; 协议的产生背景 协议的标准化OSI模型参考OSI各个分层的作用各层次的功能简介 TCP/…

Stream流性能分析及优雅使用

文章目录 摘要一、Stream原理解析1.1、Stream总概1.2、Stream运行机制1.2.1、创建结点1.2.1、搭建流水线1.2.3、启动流水线 1.3、ParallelStream 二、性能对比三、优雅使用3.1 Collectors.toMap()3.2 findFirst()&#xff0c;findAny()3.3 增删元素3.4 ParallelStream 四、总结…

【和春笋一起学C++】(十七)C++函数新特性——内联函数和引用变量

C提供了新的函数特性&#xff0c;使之有别于C语言。主要包括&#xff1a; 内联函数&#xff1b;按引用传递变量&#xff1b;默认参数值&#xff1b;函数重载&#xff08;多态&#xff09;&#xff1b;模版函数&#xff1b; 因篇幅限制&#xff0c;本文首先介绍内联函数和引用…

proteus新建工程

1 点击新建工程 2 输入项目名&#xff0c;选择工程文件夹 3 下一步 4 不创建pcb 5 直接下一步 6 点击完成 7 创建完毕

RTC实时时钟DS1338Z-33/PT7C433833WEX国产替代FRTC1338S

FRTC1338S是NYFEA徕飞公司推出的一种高性能的实时时钟芯片&#xff0c;它采用了SOP8封装技术&#xff0c;这种技术因其紧凑的尺寸和出色的性能而被广泛应用于各类电子设备中。 FRTC1338S串行实时时钟(RTC)是一种低功耗的全二进制编码十进制(BCD)时钟/日历外加56字节的非易失性…

Redis命令使用

Redis是以键值对进行数据存储的&#xff0c;添加数据和查找数据最常用的2个指令就是set和get。 set&#xff1a;set指令用来添加数据。把key和value存储进去。get&#xff1a;get指令用来查找相应的键所对应的值。根据key来取value。 首先&#xff0c;我们先进入到redis客户端…

【免费数据】1980-2022年中国2384个站点的水质数据

水&#xff0c;是生命之源&#xff0c;关乎着地球上每一个生物的生存与发展。健康的水生生态系统维持着整个水生态的平衡与活力&#xff1b;更是确保人类能持续获得清洁水源的重要保障。水质数据在水质研究、海洋生物量测算以及生物多样性评估等诸多关键领域都扮演着举足轻重的…

Git 极简使用指南

Git 是一个强大的分布式版本控制系统&#xff0c;但入门只需要掌握几个核心概念和命令。本指南旨在帮助你快速上手&#xff0c;处理日常开发中最常见的 80% 的场景。 核心概念 仓库 (Repository / Repo): 你的项目文件夹&#xff0c;包含了项目的所有文件和完整的历史记录。…

力扣刷题Day 69:搜索二维矩阵(74)

1.题目描述 2.思路 首先判断target是否有可能在矩阵的某一行里&#xff0c;没可能直接返回False&#xff0c;有可能就在这一行里二分查找。 3.代码&#xff08;Python3&#xff09; class Solution:def searchMatrix(self, matrix: List[List[int]], target: int) -> boo…

MySQL指令个人笔记

MySQL学习&#xff0c;SQL语言笔记 一、MySQL 1.1 启动、停止 启动 net start mysql83停止 net stop mysql831.2 连接、断开 连接 mysql -h localhost -P 3306 -u root -p断开 exit或者ctrlc 二、DDL 2.1 库管理 2.1.1 直接创建库 使用默认字符集和排序方式&#xf…

2022年 国内税务年鉴PDF电子版Excel

2022年 国内税务年鉴PDF电子版Excelhttps://download.csdn.net/download/2401_84585615/89784658 https://download.csdn.net/download/2401_84585615/89784658 2022年国内税务年鉴是对中国税收政策、税制改革和税务管理实践的全面总结。这份年鉴详细记录了中国税收系统的整体状…

基于Java的OPCDA采集中间件

1.软件功能及技术特点简介&#xff1a; 软件功能及技术特点简介&#xff1a; OPCDA是基于Java语言开发的OPC client&#xff08;OPC客户端&#xff09;跨平台中间件软件&#xff0c;他支持OPC SERVER的OPC DA1.0/2.0/3.0。OPCDA实时采集数据&#xff08;包括实时数据、报警数…

vue2 项目中 npm run dev 运行98% after emitting CopyPlugin 卡死

今天在运行项目时&#xff0c;发现如下问题&#xff1a; 开始以为是node_modules依赖的问题&#xff0c;于是重新 npm install&#xff0c;重启项目后还是未解决。 在网上找了一圈发现有人说是 require引入图片地址没有写。在我的项目中排查没有这个问题&#xff0c;最后发现某…

JavaScript 性能优化实战:从原理到框架的全栈优化指南

在 Web 应用复杂度指数级增长的今天&#xff0c;JavaScript 性能优化已成为衡量前端工程质量的核心指标。本文将结合现代浏览器引擎特性与一线大厂实践经验&#xff0c;构建从基础原理到框架定制的完整优化体系&#xff0c;助你打造高性能 Web 应用。 一、性能优化基础&#x…

2025年- H61-Lc169--74.搜索二维矩阵(二分查找)--Java版

1.题目描述 2.思路 方法一&#xff1a; 定义其实坐标&#xff0c;右上角的元素&#xff08;0&#xff0c;n-1&#xff09;。进入while循环&#xff08;注意边界条件&#xff0c;行数小于m&#xff0c;列数要&#xff1e;0&#xff09;从右上角开始开始向左遍历&#xff08;比当…

【黄金评论】美元走强压制金价:基于NLP政策因子与ARIMA-GARCH的联动效应解析

一、基本面&#xff1a;多因子模型解析黄金承压逻辑 1. 政策冲击因子驱动美元强势 通过NLP模型对关税政策文本进行情感分析&#xff0c;构建政策不确定性指数&#xff08;PUI&#xff09;达89.3&#xff0c;触发美元避险需求溢价。DSGE模型模拟显示&#xff0c;钢铁关税上调至…