LeetCode 242.有效的字母异位词
题目链接🔗:242.有效的字母异位词
解题思路🤔
排序法
分析题目,有效的字母异位词实质上是指两个字符串排序后相等,因此可以对两个字符串分别进行排序,再比对排序后的字符串即可。
哈希表法
题目中明确只存在小写字母,因此可以设置一个大小为26的数组来接收元素,26代表着小写字母a
-z
,0代表a
,25代表z
,将数组的元素初始化为0;
遍历第一个字符串,每经过一个元素就计算一下其与a
的ASCII相对值,这样就能得到这个元素是哪一个字母,随后在数组对应的位置上+1;
随后遍历第二个字符串,每经过一个元素就计算一下其与a
的ASCII相对值,这样就能得到这个元素是哪一个字母,随后在数组对应的位置上-1;
两次遍历完成后,只需要统计一下数组内是否有元素不为0,如果存在元素不为0,就说明两个字符串中存在不一样的字母,也就是说有字符串多了/少了字母,此时就可以返回false,否则返回true。
遇到的问题😢
只想到了排序法,看了卡哥题解后恍然大悟。
代码实现👨🏻💻
排序法
var isAnagram = function (s, t) {
let len1 = s.length; // 串s的长度
let len2 = t.length; // 串t的长度
let s1 = [...s].sort().join(''); // 将s中的元素存入数组并用Spread操作符展开,排序,将数组中元素转换为字符串,定义s1接收字符串
let t1 = [...t].sort().join(''); // 将t中的元素存入数组并用Spread操作符展开,排序,将数组中元素转换为字符串,定义t1接收字符串
if (len1 === len2 && s1 === t1) { // 如果串长度相等,同时排序后两串相等,返回true,否则返回false
return true;
} else {
return false;
}
};
哈希表法
var isAnagram = function (s, t) {
if (s.length !== t.length) return false; // 长度不相同,返回false
const res = new Array(26).fill(0); // 定义数组用于接收元素,初始化为0
const k = "a".charCodeAt(); // 定义k,接收与字符串元素进行对比的a的ASCII码
// 遍历串s,计算每个元素与k的差值,并在对应的位置+1
for(let i = 0; i < s.length; i++) res[s[i].charCodeAt() - k]++;
// 遍历串t,计算每个元素与k的差值,并在对应的位置-1
for(let i = 0; i < t.length; i++) res[t[i].charCodeAt() - k]--;
// 如果res数组中存在元素不为0,返回false
for(let i = 0; i < res.length; i++){
if(res[i] != 0) return false;
}
return true;
};
总结📖
排序法
排序法更需要对数据的敏锐,发现有效的字母异位词实质上是指两个字符串排序后相等这一条件即可解题。
哈希表法
典型的空间换时间思路,利用ASCII码进行对比比较巧妙。
LeetCode 349. 两个数组的交集
题目链接🔗:349. 两个数组的交集
解题思路🤔
依然是空间换时间的思路,定义一个numSet
用于对比,在其中存入nums1
的元素;再定义一个resSet
用于接收交集元素。
遍历nums2
,将其与numSet
中元素进行对比,当发现numSet
中存在与nums2
相同的元素是时,将元素放入resSet
中,最后返回resSet
即可。
一个优化的思路是将较长的数组存入numSet
中,这样能减少对比时的循环次数。
遇到的问题😢
无
代码实现👨🏻💻
哈希表法
var intersection = function (nums1, nums2) {
// 优化思路:将较长的数组存入numSet中,减少对比时的循环次数。
// if (nums1.length < nums2.length) {
// let temp = nums1;
// nums1 = nums2;
// nums2 = temp;
// }
const numSet = new Set(nums1); // numSet存放nums1的元素,用于对比
const resSet = new Set(); // 将其存入对比后相同的元素
// 遍历nums2,has()方法用于判断是否存在与nums2相同的元素,add()方法用于向resSet中插入保存
for (let i = 0; i < nums2.length; i++){
if(numSet.has(nums2[i])) {
resSet.add(nums2[i]);
}
}
return Array.from(resSet); // 得到的结果是Set结构,最后还需要转化为数组输出
};
总结📖
哈希表法
有了242.有效的字母异位词的基础后,这道题的思路就很好想了,有两个注意点:
- 对于JS来说,Set比数组需要适合这道题,因为我们不知道
nums1
与nums2
的大小,在较大的情况下,数组很容易造成内存浪费 - 使用
Set
时,最后返回时需要转换为数组再返回。
LeetCode 第202题. 快乐数
题目链接🔗:第202题. 快乐数
解题思路🤔
根据题目,非快乐数的平方和会出现无限循环,因此,当我们判断出一个元素的平方和进入了无限循环,就可以判定出这个元素不是快乐数,更进一步,如果我们发现这个数的平方和出现了第二次,那就说明它已经进入循环了,即可做出判断。
那么要处理的情况有如下几种:
- n = 1。此时直接返回true;
- n != 1,且平方和出现了第二次;
- n != 1,且平方和未出现第二次。
为了处理平方和出现次数,定义一个Set
用于保存元素的平方和sum
,然后对n
进行平方计算,如果Set
中出现了sum
,说明平方和出现了第二次;否则就向Set
中加入sum
,继续进行循环。
遇到的问题😢
无
代码实现👨🏻💻
哈希表法
var isHappy = function(n) {
// 将获取平方和封装为函数
const getSum = function(n){
let sum = 0;
while(n) {
sum += (n % 10) * (n % 10); // 计算个位的平方
n = Math.floor(n / 10); // 计算更高位的平方
}
return sum;
};
let set = new Set(); // 定义Set接收平方和sum
while(true){ // 循环开始
let sum = getSum(n); // 获取平方和
if (sum === 1) return true; // sum = 1与 n = 1其实是一样的,用哪个都可以
if (set.has(sum)) { // 发现Set中存在sum,返回false
return false
} else { // 未发现存在sum,向Set中插入sum
set.add(sum);
}
n = sum; // 一次对比后,将n更新为sum
}
};
总结📖
哈希表法
第一个需要注意的是处理平方和sum
时的逻辑,如果sum
在Set中出现了两次,就说明进入循环了,这时就要返回fasle了,如果没出现,将sum
添加到Set中,因为一开始Set是没有添加元素的,我们是通过循环来给Set添加元素。
第二个需要注意的是如何计算n
每一位的平方和,这里先利用取余计算出个位的平方和,然后将n缩小十倍并向下取整,再次进入循环,计算此时个位的平方和并将结果加入sum
,以此类推最后计算出n所有位的平方和。
LeetCode 1. 两数之和
题目链接🔗:1. 两数之和
解题思路🤔
分析题目,发现我们不止要取到数组中元素的下标,同时也要取得元素的值,因此可以使用Map来存放遍历过的元素,这样就可以避免使用两个数组分别存放了,降低内存的压力。
接下来需要确定key与value分别代表什么,有两种对应关系:
- key:存放元素的下标,value:存放元素的值
- key:存放元素的值,value:存放元素的下标
首先要明确,在比对时,使用的是Map的key
而不是value
,而题目中要求我们找出target
,这就代表着我们要用Map的key
来与target
进行对比,target
是数组元素的值,因此,这里我们就需要使用第二种对应关系,即:
key:存放元素的值,value:存放元素的下标
接下来考虑对比的判断逻辑就好了:
首先target
的判断可以用target - nums[i]
,这表示寻找另一个加数位置,如果在Map中它对应的key
不是undefined
,就说明找到了target
。
为什么呢?如果存在target
,我们假设数组中一个加数为nums[i]
,另一个加数为x
,一个显而易见的逻辑就是x + nums[i] = target
,此时x
必须是数组中已经存在的元素,如果它不在数组中,target
自然也就不存在了。
这里计算出的x
就可以用来与Map中的key
进行比对,如果x
是Map中的key
,也就是说map.has(target - nums[i]) != undefined
,此时的i
与target - nums[i]
就是target
的两个加数!
回到判断逻辑,如果没找到匹配的值,就继续遍历数组寻找;否则就把访问过的元素及其下标加入到Map中并返回。
最后,如果一直没找到,返回空即可。
遇到的问题😢
研究判断逻辑费了很大劲,最后看了卡哥的题解才想通。
代码实现👨🏻💻
第一种方法
var twoSum = function(nums, target) {
const map = new Map(); // 构造Map()
for(let i = 0; i < nums.length; i++) { // 遍历数组
let addEnd = target - nums[i]; // 让变量addEnd接收另搞一个加数
if(map.has(addEnd)){ // 如果addEnd在Map中存在,匹配成功
return [map.get(addEnd), i]; // 返回两个加数,get()方法:获取Map中的value
} else {
map.set(nums[i], i); // 匹配失败,向map中添加key与value
}
}
return []; // 如果一直匹配失败,返回空数组
};
总结📖
哈希表法
四个重点:
- 为什么会想到用哈希表
- 当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法
- 哈希表为什么用map
- 题目要求找到两个不同的目标,k-v键值对更易于保存
- 本题map是用来存什么的
- Map()
- map中的key和value用来存什么的
- key:存放元素的值,value:存放元素的下标
在回答出上面四点后,接下来比较重要的是找到判断的逻辑,其次是要对map足够熟悉,key与value不能搞混。
今日收获
- 重新复习了一下哈希表的几个相关数据结构
- 在解题时,对哈希表数据结构挺关键,要根据针对不同的需求使用不同的数据结构