Java算法OJ(10)哈希表练习

Java算法OJ(10)哈希表练习

目录

1.前言

2.正文

2.1俩数之和

2.2无重复字符的最长子串

2.3罗马数字转整数

2.4整数转罗马数字

3.小结


1.前言

哈喽大家好吖,今天来分享几道哈希表相关的练习题,操作比较基础但是思想比较重要,另外有许多思路与解法都是学习参照题解中诸位大佬的做法,都很有思路对我们很有启发性,欢迎大家在评论区多多交流,废话不多说让我们直接开始吧。

2.正文

2.1俩数之和

提交链接:

1. 两数之和 - 力扣(LeetCode)https://leetcode.cn/problems/two-sum/description/?envType=problem-list-v2&envId=hash-table

这道题当然无脑遍历肯定能直接解出来,但既然要锻炼哈希表的熟练程度就用HashSet来完成:

先初始化该哈希表。遍历,如果存在一个数组的数,满足目标值减去当下遍历到的这个数,那么存在解。如果遍历到最后都没有返回,那么无解。
class Solution { public int[] twoSum(int[] nums, int target) { Map <Integer,Integer>hashMap = new HashMap<>(); for(int i = 0;i < nums.length;i++){ if(hashMap.containsKey(target - nums[i])){ return new int[]{hashMap.get(target - nums[i]),i}; } hashMap.put(nums[i],i); } return new int[0]; } }
时间复杂度:遍历数组只需 O(n)O(n)O(n)。哈希表的插入和查找操作平均时间复杂度是 O(1)O(1)O(1)。总体时间复杂度:O(n)O(n)O(n)空间复杂度:额外使用了一个哈希表存储数组中的元素和索引,最坏情况下需要存储 nnn 个元素。空间复杂度:O(n)O(n)O(n)

2.2无重复字符的最长子串

3. 无重复字符的最长子串 - 力扣(LeetCode)https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/?envType=problem-list-v2&envId=hash-table

这道题运用了滑动窗口的思想,即有俩个左右指针通过移动,记录答案后找到满足题意的解。 

窗口的定义:滑动窗口是字符串的一个子串,窗口的两端由两个指针(iright)表示。窗口的内容始终保持无重复字符。双指针移动规则右指针 right:用于扩展窗口,表示当前扫描到的位置。左指针 i:用于收缩窗口,解决窗口内出现重复字符的问题。用数据结构维护窗口状态:使用一个 HashSet(或哈希表)存储窗口内的字符,快速判断窗口中是否已经包含某个字符。过程描述:初始化窗口左右边界(iright),以及存储结果的变量(ans)。遍历字符串,用右指针逐步扩展窗口;如果窗口内出现重复字符,用左指针逐步缩小窗口,直到窗口重新无重复。在每一步中,更新窗口的长度并维护最长子串的长度。结束条件:当右指针到达字符串末尾时,遍历结束,返回最长子串的长度。
class Solution { public int lengthOfLongestSubstring(String s) { Set<Character> set = new HashSet<Character>(); int right = 0; int ans = 0; for(int i = 0;i < s.length();i++){ if(i != 0){ set.remove(s.charAt(i - 1));//左指针 } while(right < s.length() && !set.contains(s.charAt(right))){ set.add(s.charAt(right)); right++; } ans = Math.max(ans,(right - i)); } return ans; } }
时间复杂度:每个字符至多被访问两次(一次被左指针移除,一次被右指针添加)。总时间复杂度为 O(n)O(n)O(n)。空间复杂度:使用了 HashSet 存储字符,空间复杂度为 O(k)O(k)O(k)。

2.3罗马数字转整数

13. 罗马数字转整数 - 力扣(LeetCode)https://leetcode.cn/problems/roman-to-integer/description/?envType=problem-list-v2&envId=hash-table

思路如下:

首先我们先认真读题,理解罗马数字的一些规则。罗马数字使用字符表示数值:
I=1, V=5, X=10, L=50, C=100, D=500, M=1000。大部分情况下,罗马数字字符按照从左到右从大到小排列,并直接相加。如:VII = 5 + 1 + 1 = 7但在特定情况下,较小值的字符出现在较大值字符的左边时,表示减法。如:IV = 5 - 1 = 4IX = 10 - 1 = 9

算法设计:利用一个哈希表(HashMap)存储罗马字符和整数值之间的映射关系。遍历字符串,每次提取当前字符的值,并判断是否需要减去还是加上该值。如果当前字符的值小于下一个字符的值(value < next_value),说明需要减去当前值。否则,将当前值加到结果中。

算法实现步骤初始化映射表:将每个罗马字符对应的值存入一个哈希表中,便于快速查找。遍历字符串:当前字符值(value)与下一个字符值(next_value)进行比较:如果 value < next_value,结果减去当前值;否则,结果加上当前值。循环直到字符串结束。返回结果:遍历完成后,累积的结果即为整数值。
class Solution { public int romanToInt(String s) { Map<Character, Integer> map = new HashMap<Character, Integer>() {{ put('I', 1); put('V', 5); put('X', 10); put('L', 50); put('C', 100); put('D', 500); put('M', 1000); }}; int ans = 0; int n = s.length(); for(int i = 0;i < s.length();i++){ int value = map.get(s.charAt(i)); if(i < n - 1 && value < map.get(s.charAt(i + 1))){ ans -= value; } else { ans += value; } } return ans; } }
时间复杂度:O(n)O(n)O(n),其中 nnn 是罗马数字字符串的长度,每个字符只访问一次。空间复杂度:O(1)O(1)O(1),哈希表的大小是固定的,与输入规模无关。

2.4整数转罗马数字

12. 整数转罗马数字 - 力扣(LeetCode)https://leetcode.cn/problems/integer-to-roman/description/?envType=problem-list-v2&envId=hash-table

这道题比较关键的是把这个哈希表建立出来,题目中给的并不够,只罗列出关键的 7 种数字字符,加上 6 种特殊数字所对应的罗马数字,就足够了:

建立映射关系:使用两个数组 valuessymbols 分别存储数值与对应的罗马符号,并按照数值从大到小排列。

遍历处理:从高到低依次处理每一个罗马数字的数值和符号。如果当前数值可以被整数 num 包含,减去该值,并将对应的罗马符号添加到结果字符串中。重复处理当前符号,直到 num 小于该数值。

提前结束:如果整数 num 减为 0,则提前退出循环以优化效率。

返回结果:最终拼接完成后返回结果字符串。
class Solution { // 定义数值与罗马数字的对应关系 int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; public String intToRoman(int num) { StringBuilder roman = new StringBuilder(); // 使用 StringBuilder 拼接字符串 for (int i = 0; i < values.length; ++i) { while (num >= values[i]) { num -= values[i]; // 减去当前数值 roman.append(symbols[i]); // 添加对应的罗马符号 } if (num == 0) break; // 如果数字为 0,提前退出循环 } return roman.toString(); // 返回最终罗马数字 } } 
时间复杂度:O(1)O(1)O(1)
罗马数字的种类和数量是固定的,因此算法的循环次数是常数,与输入大小无关。空间复杂度:O(1)O(1)O(1)
除了固定大小的数组和结果字符串外,额外的空间使用量与输入无关。

3.小结

今天的分享到这里就结束了,喜欢的小伙伴不要忘记点点赞点个关注,你的鼓励就是对我最大的支持,加油!

Read more

【C++】unordered系列容器使用及封装

【C++】unordered系列容器使用及封装

目录 一、unordered_map和unordered_set的使用 1. unordered_set系列的使用 1.1 unordered_set和unordered_multiset参考文档 1.2 unordered_set类的介绍 1.3 unordered_set和set的使用差异 1.4 unordered_map和map的使用差异 1.5 unordered_multimap/unordered_multiset 1.6 unordered_xxx的哈希相关接口 二、用哈希表封装myunordered_map和myunordered_set 1. 源码及框架分析 2. 模拟实现unordered_map和unordered_set 2.1 实现出复用哈希表的框架,并支持insert 2.

By Ne0inhk
扒透 STL 底层!map/set 如何封装红黑树?迭代器逻辑 + 键值限制全手撕----《Hello C++ Wrold!》(23)--(C/C++)

扒透 STL 底层!map/set 如何封装红黑树?迭代器逻辑 + 键值限制全手撕----《Hello C++ Wrold!》(23)--(C/C++)

文章目录 * 前言 * map和set的封装 * 底层红黑树的模拟实现 * 迭代器的模拟实现 前言 你是不是也有过这种 “知其然不知其所以然” 的困惑: 用 map 存键值对、用 set 去重排序时很顺手,但一被问 “map 的 [] 怎么既插入又访问”“set 为啥不能改元素”“它们底层的红黑树到底存的啥”,就瞬间卡壳?甚至看 STL 源码时,被 “KeyOfT”“迭代器 ++ 逻辑” 绕得晕头转向? 其实 map 和 set 的本质,就是对红黑树的 “定制化封装” —— 红黑树是 “通用骨架”,map 和 set 通过 “提取键的规则(KeyOfT)”“迭代器权限控制”“键值修改限制”,分别适配了 “键值对存储”

By Ne0inhk
【C++初阶】C++入门相关知识(1):C++历史 & 第一个C++程序 & 命名空间

【C++初阶】C++入门相关知识(1):C++历史 & 第一个C++程序 & 命名空间

🎈主页传送门:良木生香 🔥个人专栏:《C语言》 《数据结构-初阶》 《程序设计》 🌟人为善,福随未至,祸已远行;人为恶,祸虽未至,福已远离 前言:我们在此之前已经学习了C语言和数据结构,明白了C语言的基本概念,同时也学习了初阶的数据结构,现在,我们已经具备了学习初阶c++的能力了,那么,从今天开始,我们就正式进入到C++的学习中了,本专栏会记录下小编的学习C++的历程,有什么讲的不对的地方还请大佬们指出错误,那么,现在我们就正式进入到C++的学习吧 本专栏介绍:在我们之前已经学习过的C语言和数据结构的基础上,我们将会在本C++专栏上继续学习C++语法、STL、以及高阶数据结构 目录 一、C++历史介绍 1.1、起源与诞生(1979~1983) 1.2、核心 1.3发展与完善(

By Ne0inhk

CCF-GESP计算机学会等级考试2025年9月二级C++T2 菱形

B4412 [GESP202509 二级] 菱形 题目描述 小 A 想绘制一个菱形。具体来说,需要绘制的菱形是一个 nnn 行 nnn 列的字符画,nnn 是一个大于 111 的奇数。菱形的四个顶点依次位于第 111 行、第 111 列、第 nnn 行、第 nnn 列的正中间,使用 # 绘制。相邻顶点之间也用 # 连接。其余位置都是 .。 例如,一个 555 行 555 列的菱形字符画是这样的: ..#.. .#.#. #...# .#.#. ..#.. 给定 nnn,请你帮小 A 绘制对应的菱形。 输入格式 一行,一个正整数 nnn。

By Ne0inhk