今天是哈希表练习的第一天:
首先复习了哈希表的理论基础:
哈希表是根据关键码的值直接进行访问的数据结构,一般解决的问题是判断一个元素是否出现在集合中,有数组,map和set这几种
如果hashCode得到的数值大于tableSize,那么就要对数组进行一个取模操作,通过哈希函数来映射到哈希表的索引;
如果发生了哈希碰撞(即两个名字都被链接到了同一个index, 那么需要用其他方法来解决问题 (拉链法和线性探索法)
因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
242JS代码实现
var isAnagram = function (s, t) {
let letterArray = new Array(26).fill(0);
const base = 'a'.charCodeAt() // js这里求unicode要用charCodeAt
for (let i = 0; i < s.length; i = i + 1) {
letterArray[s[i].charCodeAt() - base]++;
}
for (let j = 0; j < t.length; j = j + 1) {
letterArray[t[j].charCodeAt() - base]--;
}
for (let k = 0; k < 26; k++) {
if (letterArray[k] !== 0) {
return false;
}
}
return true;
};
242. 有效的字母异位词
题目链接:. - 力扣(LeetCode)
哈希表的结构因为平常用到的比较多,相对会熟悉一点;
这道题我的思路是:
1. 用一个哈希表去存s的每一个字符的次数,得到s的里面每个字母的出现次数
2. 再用这个哈希表判断是否存在t里面的字母,将t字母每次出现的字母-1,
3. 如果这个哈希表最后每个字母对应的值都为0的话,则为一个字母异位词
4. 这个解法的空间复杂度为O(n + m), 这个解法的时间复杂度为O(n + m))
实现代码如下:
function areAnagrams(s, t) {
const map = new Map();
// 统计字符串 s 中每个字符出现的次数
for (let i = 0; i < s.length; i++) {
const char = s[i];
map.set(char, (map.get(char) || 0) + 1);
}
// 在字符串 t 中减去字符出现的次数
for (let j = 0; j < t.length; j++) {
const char = t[j];
if (!map.has(char)) {
return false; // 如果 t 中出现了 s 中没有的字符,则不是字母异位词
}
map.set(char, map.get(char) - 1);
if (map.get(char) === 0) {
map.delete(char); // 如果字符的次数减到 0,则从 Map 中删除
}
}
// 如果 Map 不为空,则表示有字符在 s 中但不在 t 中,或者在 t 中但不在 s 中
return map.size === 0;
}
卡哥思路: 有些方法需要基础知识掌握的比较牢固;因为这两个字符串都是由字母组成的,字母a-z对应的ASCII码是连续的,那么如果拿这个字母去减去a,就可以对应到数组的索引。
另外卡哥提到:一般哈希法可以先想想能不能用数组,但是数据量比较大的时候,就不适合用数组了。先要考虑数组,因为速度是最快的。
369.两个数组的交集
题目链接: . - 力扣(LeetCode)
我的思路:
1. 本来想根据有效的字母异位词改一改,根据长数组来判断短数组中是否存在相对应的值,但是长数组里面可能存在重复的值,那用map来统计次数则不对了,
2. 则通过遍历长数组,将其值映射成map的形式,再遍历短数组,如果map中有相对应的key,则证明其为相交值,则push进入result
3. 还有一点,如果短数组中有重复项的话,那result也会重复,所以保证唯一性的话,应该开始就用Set去定义
var intersection = function (nums1, nums2) {
let length1 = nums1.length;
let length2 = nums2.length;
let setNum1 = new Set(nums1);
let setNum2 = new Set(nums2);
let longArr = length1 > length2 ? setNum1 : setNum2;
let shortArr = length1 > length2 ? setNum2 : setNum1;
let result = [];
console.log("short", longArr, shortArr);
for (let num of longArr) {
if (shortArr.has(num)) {
result.push(num);
}
}
return result;
};
卡哥思路:
上一题提过如果可以用哈希表,那么可以优先考虑数组;并且乐扣设定了 num 的长度在 1000 以下,数据量较少的情况下适合用数组,数组操作更快
数组方式的实现代码:
var intersection = function(nums1, nums2) {
let lengthArray = new Array(1005).fill(0)
let result = []
for(let i = 0; i < nums1.length; i++) {
lengthArray[nums1[i]] = 1
}
for(let j = 0; j < nums2.length; j++) {
if(lengthArray[nums2[j]] === 1) {
result.push(nums2[j])
}
}
return Array.from(new Set(result))
};
202.快乐数
题目链接:. - 力扣(LeetCode)
我的思路:
用一个set来存得出的位数平方的和, 如果在set中能找到当前数得出的平方和,那么则会陷入无限循环;则返回 false,那么用 while 循环判断是否是进入无限循环
const getSum = (n) => {
let sum = 0;
while (n) {
sum = sum + (n % 10) * (n % 10)
n = Math.floor(n / 10)
}
return sum
}
var isHappy = function(n) {
let set = new Set()
while(true) {
if(set.has(n)) return false
if(n === 1) return true
set.add(n)
n = getSum(n) // 下一次的 n 是各位数求和之后的值
}
};
1. 两数之和
题目链接:. - 力扣(LeetCode)
思路:用一个 map 来存 索引(key)和 target 和当前索引对应的差值(value),然后遍历数组,如果当前索引不是一样的,且和差值相等,则得到两个数
let map = new Map()
for(let i = 0; i < nums.length; i++) {
map.set(target - nums[i], i)
}
for(let j = 0; j < nums.length; j++) {
if(map.get(nums[j]) && j !== map.get(nums[j])) {
return [map.get(nums[j]), j]
}
}
return []