代码随想录day06,哈希表part1

哈希表理论基础

哈希表是根据关键码的值而直接进行访问的数据结构。哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素。一般哈希表都是用来快速判断一个元素是否出现集合里。

通过哈希函数把数组中的数字直接映射为哈希表上的索引,然后就可以通过查询索引下标快速知道数字位置。

哈希函数一般是通过特定编码方式,可以将其他数据格式转化为不同的数值。比如说想要把[123456],如果将每个数字减1,就可以将他们映射到另一个数组[012345],另一个数组上的位置就是他们的索引。

但是可能会发生哈希碰撞,比如,两个数字都映射到了1,那就会发生碰撞。一般哈希碰撞有两种解决方法, 拉链法和线性探测法。

拉链法

拉链法就是,如果在索引1发生碰撞,那就是说有两个元素都映射到了1,那就在1这个位置存储在链表上。拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。

线性探测法

使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。

就是小李小王都映射到了1,上面是横着用链表把他俩都放下,这个就是竖着把他俩都放下。例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息。所以要求tableSize一定要大于dataSize ,要不然哈希表上就没有空置的位置来存放 冲突的数据了。

当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。

  • 数组
  • set (集合)
  • map(映射)

当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。

242.有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

示例 1: 输入: s = "anagram", t = "nagaram" 输出: true

示例 2: 输入: s = "rat", t = "car" 输出: false

字母异位词就是排列,相当于,输入一个串 S,一个串 T,判断T是不是S中的一种排列

数组其实就是一个简单哈希表,而且这道题目中字符串只有小写字符,那么就可以定义一个数组,来记录字符串s里字符出现的次数。

数组大小为26的record数组,初始化为0,因为字符a到字符z的ASCII也是26个连续的数值。

用这个record数组来记录字符串s里字符出现的次数,s 里出现的字母 +1,t 里出现的字母 −1,最后 26 个槽全为 0 才能说明是字母异位词。

s和t字符出现的次数一样,那么他们两个肯定是同样的字符进行不同排列的字符串。如果不一样,那连字符都不一样,更不用说是相同字符的不同排列了。

class Solution{ public boolean isAnagram(String s, String t) { int[] record = new int[26]; for(int i=0;i<s.length();i++){ //统计s字符出现次数 //s.charAt(i):从字符串s中取出第i个字符 //并不需要记住字符a的ASCII,只要求出一个相对数值就可以了 // record[... ]++:对映射后的索引位置的值做加 1操作 record[s.charAt(i) - 'a']++; } //统计t字符出现字符, for(int i = 0;i<t.length();i++){ record[t.charAt(i) - 'a']--; } for(int count:record){ if(count !=0){ return false; } } return true; } } 

349. 两个数组的交集

给定两个数组,编写一个函数来计算它们的交集。

示例 1:

 输入:nums1 = [1,2,2,1], nums2 = [2,2]  输出:[2]

输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序

这个题增添了数值范围:

  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 1000

题目限制了数值大小,所以可以采用数组求解。

如果题目没有限制数值的大小,就无法使用数组来做哈希表了。因为使用数组来做哈希的题目,是因为题目都限制了数值的大小而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。这时候用set来解决。

但是不能什么哈希问题都用set,因为直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。

使用数组求解:

class Solution{ public int[] intersection(int[] nums1, int[] nums2) { //1002是因为比最大元素值1000多2,预留冗余避免越界 int[] hash1 = new int[1002]; int[] hash2 = new int[1002]; //遍历数组nums1的每一个元素,计数在nums1 中出现的次数 for(int i:nums1) hash1[i]++; for(int i:nums2) hash2[i]++; //创建动态整数列表,收集两个数组的交集元素 List<Integer> resList = new ArrayList<>(); for(int i=0;i<1002;i++){ //交集元素+1 if(hash1[i]>0&&hash2[i]>0) resList.add(i); } //将存储交集元素的 List 集合(resList),转换为最终需要返回的 int 类型数组 int res[] = new int[resList.size()]; for(int j = 0; j < resList.size(); j++){ res[j] = resList.get(j); } return res; } }

写法2:用 HashSet 去重,再 retainAll 取交集,不受数据范围限制

class Solution { public int[] intersection(int[] nums1, int[] nums2) { //注意和上面对比,遍历的逻辑一样,只是换成set Set<Integer> set1 = new HashSet<>(); Set<Integer> set2 = new HashSet<>(); for (int n : nums1) set1.add(n); for (int n : nums2) set2.add(n); //保留 set1 中与 set2 共有的元素,删除 set1 中独有的元素 set1.retainAll(set2); // 取交集 //创建结果数组 int[] res = new int[set1.size()]; //遍历 set1 中的交集元素,通过一个计数变量 i 把元素依次存入 res 数组 int i = 0; for (int n : set1) res[i++] = n; return res; } }

第202题. 快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。

如果 n 是快乐数就返回 True ;不是,则返回 False 。

示例:

输入:19。输出:true 解释: 1^2 + 9^2 = 82 8^2 + 2^2 = 68 6^2 + 8^2 = 100 1^2 + 0^2 + 0^2 = 1

题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要!

这道题目使用哈希法,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。还有一个难点就是求和的过程,如果对取数值各个位上的单数操作不熟悉的话,做这道题也会比较艰难。

class Solution{ public boolean isHappy(int n){ Set<Integer> record = new HashSet<>(); //如果当前数n已存在于record(存储过的中间结果),说明进入无限循环(永远到不了1),终止循环 while(n!=1 &&!record.contains(n)){ record.add(n); n = getNextNumber(n); } return n==1; } private int getNextNumber(int n) { int res = 0; //计算一个整数 n 所有位上数字的平方和 while(n>0){ int temp = n%10;//个位 res +=temp*temp;//个位平方和 n=n/10;//去掉个位,接下来循环算十位,直到变成0 } return res; } }

1. 两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例 :

 输入:nums = [2,7,11,15], target = 9  输出:[0,1]  解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法。

本题,不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适

遍历当前元素,并在map中寻找是否有匹配的key,计算互补元素temp,判断哈希表map中是否存在互补元素temp,存在就找到了答案。将当前元素的下标i存入res的第二个位置,获取互补元素temp在数组中的下标,并存入res的第一个位置。

res[1]存储后遍历到的元素的下标(即 i),因为 i是当前循环的索引,比哈希表中存储的 j(map.get(temp))更大。

//使用哈希表 public int[] twoSum(int[] nums, int target) { int[] res = new int[2];//结果数组 if(nums == null || nums.length == 0){ return res; } Map<Integer, Integer> map = new HashMap<>(); for(int i = 0; i < nums.length; i++){ // 遍历当前元素,并在map中寻找是否有匹配的key //计算互补元素temp int temp = target - nums[i]; //判断哈希表map中是否存在互补元素temp,存在就找到了答案 if(map.containsKey(temp)){ //将当前元素的下标i存入res的第二个位置 res[1] = i; //获取互补元素temp在数组中的下标,并存入res的第一个位置; res[0] = map.get(temp); break; } // 如果没找到匹配对,就把访问过的元素和下标加入到map中 map.put(nums[i], i); } return res; }

如果题目没保证有序,就不能用双指针。如果想用双指针需要先排序,数组有序,就应该想到双指针技巧。

Read more

前端如何实现 [记住密码] 功能

前端如何实现 [记住密码] 功能

文章目录 * 一、核心实现原理:不是记住,而是“提示填充” * 二、技术实现方案详解 * 方案一:依赖浏览器原生行为(最常用) * 方案二:前端持久化存储(需谨慎考虑) * 三、安全考量与实践准则 * 四、最佳实践总结 我们在访问网站的时候,发现很多的登录页面都是有记住密码的功能的。 如gitee码云的登录页面: 一、核心实现原理:不是记住,而是“提示填充” 首先要澄清一个常见的误解:前端的“记住密码”功能通常并不直接存储你的密码明文。它的核心原理是:请求浏览器将账号密码保存到其密码管理器中,并在下次检测到对应登录表单时,自动或提示用户填充。 下图清晰地展示了这一核心流程: 服务器浏览器密码管理器登录表单用户服务器浏览器密码管理器登录表单用户首次登录与保存后续自动填充1. 输入账号密码,勾选“记住我”2. 提交表单,发送登录请求3. 返回登录成功响应4. 触发浏览器提示:“是否保存密码?”5. 用户点击“保存”6. 将账号、

By Ne0inhk
cann-recipes-train 仓库深度解读:昇腾平台下 DeepSeek-R1 与 Qwen2.5 强化学习训练优化实践

cann-recipes-train 仓库深度解读:昇腾平台下 DeepSeek-R1 与 Qwen2.5 强化学习训练优化实践

cann-recipes-train 仓库深度解读:昇腾平台下 DeepSeek-R1 与 Qwen2.5 强化学习训练优化实践 前言 自 DeepSeek-R1 发布以来,大模型的强化学习(RL)训练掀起了新一轮的技术热潮。各大厂商与开源社区纷纷投入实践,持续探索更高效的 RL 训练体系。本文将基于 cann-recipes-train 仓库,解读两个实践样例:DeepSeek-R1 的 RL 训练优化实践样例、基于 verl 框架的 Qwen2.5 强化学习实践样例 cann-recipes-train 仓库全景解析:昇腾训练优化的"实战底座" 大模型训练拼效率的阶段,CANN 直接帮我们搞定了底层异构硬件适配、资源调度这些麻烦事,不用再从零研究 GPU 和 NPU 怎么协同,现有模型代码也不用大改就能对接,训

By Ne0inhk

一个 skill ,增加大模型前端的审美能力

上周,我让 AI 帮我做个落地页。 十分钟过去了,生成出来的东西—— 白色背景,紫色渐变,Inter 字体。 我直接关了。 你也遇到过吧? 用 AI 生前端,出来的东西都长一个样。 背景非白即黑,标题栏永远是紫色渐变,字体不是 Inter 就是 Roboto,配色永远是那套蓝绿红黄。 不是说不能用,但—— 太像 AI 了。 一眼看过去就是"机器生成",没有灵魂,没有个性。 直到昨天,我发现了一个东西。 Anthropic 官方出的一个 skill,叫 frontend-design。 让我再试一次。 这次不一样了 同样的提示词,同样的模型。 我只加了一句话: “使用 frontend-design skill” 结果呢?

By Ne0inhk
在 Cursor 中打造你的专属前端“AI 助手”:Agent Skills 实战指南 什么是 Agent Skills?

在 Cursor 中打造你的专属前端“AI 助手”:Agent Skills 实战指南 什么是 Agent Skills?

文章目录 * 一、什么是 Agent Skills? * 二、使用步骤 * 1.下载官方提供的agent-skills文档 * 2.cursor中使用 * 三、如何设计自己的skills * 四、实战:打造一个“生成标准 React 组件”的 Skill * 第一步:创建目录 * 第二步:编写 SKILL.md * 总结:为什么你应该开始用 Skills? 一、什么是 Agent Skills? 简单来说,Agent Skills 是一种标准化的方式,用来封装特定任务的知识和工作流。 如果说 MCP (Model Context Protocol) 是给 AI 装上了“手”(让它能连接数据库、Github)

By Ne0inhk