当前位置: 首页 > news >正文

基础算法(9)——哈希表

1. 两数之和

题目描述:

算法思路:

解法一:暴力解法

// 解法一:暴力解法
// 方式一:固定一个数,依次与该数后面的数相加
class Solution {public int[] twoSum(int[] nums, int target) {int[] ret = new int[2];int n = nums.length;for (int i = 0; i < n; i++) {for (int j = i + 1; j < n; j++) {if (nums[i] + nums[j] == target) {ret[0] = i;ret[1] = j;return ret;}}}return ret;}
}
// 方式二:固定一个数,依次与该数前面的数相加
class Solution {public int[] twoSum(int[] nums, int target) {int[] ret = new int[2];int n = nums.length;for (int i = 0; i < n; i++) {for (int j = i - 1; j >= 0; j--) {if (nums[i] + nums[j] == target) {ret[0] = i;ret[1] = j;return ret;}}}return ret;}
}

解法二: 使用哈希表来做优化

如果基于上面暴力解法中的方式一,事先需要将所有的数字放到哈希表中,然后 for 循环 nums,判断是否有两数相加等于 target

现假设 target 为 8,在 for 循环中,遇到了 4,需要去哈希表中找有没有 4,若 nums 中就只有一个 4,那 for 循环遇到的 4 和 哈希表中的 4 就是同一个,这样是不满足条件的,所以需要加特判,不能找到自己本身相加

// 解法二:使用哈希表来做优化
// 基于暴力解法的方式一优化
class Solution {public int[] twoSum(int[] nums, int target) {// 第一步:创建哈希表,并将数组元素存入哈希表Map<Integer, Integer> numMap = new HashMap<>();for (int i = 0; i < nums.length; i++) {numMap.put(nums[i], i);}// 第二步:遍历数组,查找满足条件的两个数for (int i = 0; i < nums.length; i++) {int complement = target - nums[i];// 判断哈希表中是否存在目标数,且不是当前元素自身if (numMap.containsKey(complement) && numMap.get(complement) != i) {return new int[]{i, numMap.get(complement)};}}return new int[]{};}
}

而基于方式二进行优化,则没有这个问题,哈希表不需要提前初始化,当 for 循环遇到数字后,就去哈希表中匹配,若没有匹配到,则将当前数字添加到哈希表中(Hash<nums[i], i>),这样向后循环,不会碰到自己匹配自己的情况

// 基于暴力解法的方式二优化
class Solution {public int[] twoSum(int[] nums, int target) {// 键为数组元素值,值为元素在数组中的索引Map<Integer, Integer> numMap = new HashMap<>();for (int i = 0; i < nums.length; i++) {// 对于每个元素 nums[i],计算其与目标值 target 的差值 complementint complement = target - nums[i];// 检查 numMap 中是否包含指定的键 complementif (numMap.containsKey(complement)) {return new int[]{numMap.get(complement), i};}// 每次循环将当前元素 nums[i] 及其索引 i 存入 numMap,为后续查找做准备numMap.put(nums[i], i);}return new int[]{};}
}

2. 判定是否互为字符重排

题目描述:

算法思路

前提:先判断两字符串长度是否相等,若不等直接返回 false

不推荐的解法:

1. 找出 s1 的全排列,依次去和 s2 比较,时间复杂度是指数级别的

2. 使用 hash <char, int>,char 表示字符,int 表示出现次数,这样将 s1 和 s2 分别存到 hash1 和 hash2 中后,需要再去循环 s1,找到其中字符在 hash1 和 hash2 中出现的次数,判断是否相等

推荐解法:使用数组模拟哈希表实现

1. 使用两个哈希表,然后判断两个哈希表是否相等即可

因为题目中说明,仅有小写字母,所以仅需开辟 26 个元素大小的空间,将 s1 和 s2 分别存放到 hash1 和 hash2 中,循环比较 26 次,即可得到结果

2. 优化:使用一个哈希表 hash1 存 s1

然后遍历 s2,遇到一个字符,则在 hash1 中当前字符出现次数 -1

若减完等于负数,则不等

代码实现

class Solution {public boolean CheckPermutation(String s1, String s2) {int n1 = s1.length();int n2 = s2.length();if (n1 != n2)return false;int[] hash = new int[26];// 先把第一个字符串的信息统计到哈希表中for (int i = 0; i < n1; i++) {hash[s1.charAt(i) - 'a']++;}// 遍历第二个字符串,判断是否可以重排for (int j = 0; j < n2; j++) {hash[s2.charAt(j) - 'a']--;if (hash[s2.charAt(j) - 'a'] < 0)return false;}return true;}
}

3. 存在重复元素

题目描述:

题目解析

分析题目,出现【至少两次】的意思就是数组中存在着重复的元素,因此我们可以无需统计元素出现的数据,仅需在遍历数组的过程中,检查当前元素【是否在之前已经出现过】即可

因此我们可以利用哈希表,仅需存储【数组内的元素】,在遍历数组的时候,一边检查哈希表中是否已经出现过当前元素,一边将元素加入到哈希表中

代码实现

class Solution {public boolean containsDuplicate(int[] nums) {Set<Integer> set = new HashSet<>();for (int x : nums) {if (set.contains(x)) return true;set.add(x);}return false;}
}

4. 存在重复元素II

题目描述

题目解析

解决该问题需要我们快速定位到两个信息:

- 两个相同的元素;

- 这两个相同元素的下标。

因此,我们可以使用「哈希表」,令数组内的元素做 key 值,该元素所对应的下标做 val 值,将「数组元素」和「下标」绑定在一起,存入到「哈希表」中。

思考题: 如果数组内存在大量的「重复元素」,而我们判断下标所对应的元素是否符合条件的时候,需要将不同下标的元素作比较,怎么处理这个情况呢?

答:这里运用了一个「小贪心」。

我们按照下标「从小到大」的顺序遍历数组,当遇到两个元素相同,并且比较它们的下标时,这两个下标一定是距离最近的,因为:

- 如果当前判断符合条件直接返回 true,无需继续往后查找。

- 如果不符合条件,那么前一个下标一定不可能与后续相同元素的下标匹配(因为下标在逐渐变大),那么我们可以大胆舍去前一个存储的下标,转而将其换成新的下标,继续匹配。

代码实现

class Solution {public boolean containsNearbyDuplicate(int[] nums, int k) {Map<Integer, Integer> hash = new HashMap<>();for (int i = 0; i < nums.length; i++) {if (hash.containsKey(nums[i])) {if (i - hash.get(nums[i]) <= k) return true;}hash.put(nums[i], i);}return false;}
}

5. 字母异位词分组

题目描述

算法思路:

解法(哈希表 + 排序):

互为字母异位词的单词有一个特点:将它们「排序」之后,两个单词应该是「完全相同」的。 所以,我们可以利用这个特性,将单词按照字典序排序,如果排序后的单词相同的话,就划分到同一组中。

这时我们就要处理两个问题:

- 排序后的单词与原单词需要能互相映射;

- 将排序后相同的单词,「划分到同一组」;

利用语言提供的「容器」的强大的功能就能实现这两点:

- 将排序后的字符串(string)当做哈希表的 key 值;

- 将字母异位词数组(string[])当成 val 值。 定义一个「哈希表」即可解决问题。

代码实现:

class Solution {public List<List<String>> groupAnagrams(String[] strs) {Map<String, List<String>> hash = new HashMap<>();// 1. 异位词分组并排序for (String s : strs) {char[] tmp = s.toCharArray();Arrays.sort(tmp);String key = new String(tmp);if (!hash.containsKey(key)) {hash.put(key, new ArrayList());}hash.get(key).add(s);}//  2. 获取值return new ArrayList(hash.values());}
}
http://www.dtcms.com/a/303347.html

相关文章:

  • nginx日志分割
  • 11.Dockerfile简介
  • Java中的协变、逆变
  • 【AI绘画】Stable Diffusion webUI 与 ComfyUI 全解析:安装、模型、插件及功能对比
  • 使用宝塔“PostgreSQL管理器”安装的PostgreSQL,如何设置远程连接?
  • 开发避坑短篇(7):Vue+window.print()打印实践
  • Linux中配置haproxy
  • Java 笔记 serialVersionUID
  • 50etf的实值期权和虚值期权谁涨得快?
  • gdb调试教程
  • 图像轮廓与凸包
  • 网络编程接口htonl学习
  • 如何进行DAP-seq的数据挖掘,筛选验证位点
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现面部口罩的检测识别(C#代码,UI界面版)
  • C++-关于协程的一些思考
  • json取值,如果字段不存在,匹配下一个字段
  • 自定义View学习记录 plinko游戏View
  • 恒坤新材IPO被暂缓审议:收入确认法遭质疑,募资缩水约2亿元
  • 元宇宙经济与数字经济的异同:虚实交织下的经济范式对比
  • 基于Springboot的宠物救助管理系统的设计与实现
  • 【VUE3】搭建项目准备工作
  • 艾格文服装软件怎么用?
  • Windows中查看GPU和Cuda信息的DOS命令总结
  • AI产品经理手册(Ch1-2)AI Product Manager‘s Handbook学习笔记
  • uvm sequence Arbitration
  • AI 驱动、设施扩展、验证器强化、上线 EVM 测试网,Injective 近期动态全更新!
  • git stash apply 冲突合并方法解决
  • 希尔排序(缩小增量排序)面试专题解析
  • unisS5800XP-G交换机配置命令之登录篇
  • 洛谷 P10448 组合型枚举-普及-