代码随想录——散列表

28 阅读12分钟

哈希表

1. 哈希表理论基础

当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。

2. 有效的字母异位词

算法应用

  1. 拼写检查器:在拼写检查器中,我们需要检查一个单词是否为另一个单词的字母异位词。通过有效的字母异位词算法,我们可以检查两个单词的字符出现频率是否相同,从而判断它们是否为字母异位词。
  2. 电商推荐系统:在电商推荐系统中,我们可以使用字母异位词算法来分析用户的搜索历史和购买记录,找出相似的商品,并向用户推荐这些商品。
  3. 数据库查询:在数据库查询中,我们可以使用字母异位词算法来优化匹配查询,以提高查询效率。通过对查询条件进行归一化和标准化,我们可以将多个查询条件转化为字母异位词,从而快速地查询相关数据。
  4. 资源管理:在资源管理中,我们可以使用字母异位词算法来查找相同或相似的资源。例如,在电子文档管理系统中,我们可以使用字母异位词算法来查找相同或相似的文档,以避免重复存储和管理。
  5. 数据分析:在数据分析中,我们可以使用字母异位词算法来分析数据的特征和规律。例如,在文本分析中,我们可以使用字母异位词算法来分析文本数据中词语的出现频率和分布情况,从而提取文本的关键信息。

总之,字母异位词算法在现实生活中有着广泛的应用,可以帮助我们解决各种实际问题,提高系统效率和服务质量。
算法逻辑

/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
var isAnagram = function(s, t) {
  let record = new Array(26).fill(0);
  for (let i = 0; i < s.length; i++) {
    // 并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
    record[s[i].charCodeAt() - 'a'.charCodeAt()]++;
  }
  for (let i = 0; i < t.length; i++) {
    record[t[i].charCodeAt() - 'a'.charCodeAt()]--;
  }
  for (let i = 0; i < record.length; i++) {
    // record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
    if (record[i] != 0) {
      return false;
    }
  }
  // record数组所有元素都为零0,说明字符串s和t是字母异位词
  return true;
};

3. 两个数组的交集

算法应用

  1. 在社交网络中,我们可以通过交集算法找到我们和某个用户之间的共同好友,从而可以更好地拓展社交网络。
  2. 在电商平台中,我们可以通过交集算法找到用户购买记录中的相同商品,从而可以向用户推荐更相关的商品。
  3. 在音乐APP中,我们可以通过交集算法找到用户听歌记录中的相同歌曲,从而可以向用户推荐更相关的音乐。
  4. 在物流管理中,我们可以通过交集算法找到需要配送的货物和某个物流车辆可以运载的货物之间的共同部分,从而可以优化配送效率。
  5. 在科学研究中,我们可以通过交集算法找到不同基因组之间的共同基因,从而可以更好地了解基因对生命的影响。

交集算法的本质是找到两个集合之间的共性,通过这些共性来推出一些结论,从而可以应用到不同的场景中。
在应用中,可以根据这些共性来进行个性化定制,以更好地满足用户的需求。同时,也可以通过发现这些共性和结论,来深入研究自然规律和现实问题,从而推动科学研究和技术创新的发展。
算法逻辑
image.png

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var intersection = function(nums1, nums2) {
    const result_set = new Set();
    const nums_set = new Set(nums1);
    nums2.forEach(num => {
      if (nums_set.has(num)) {
        result_set.add(num);
      }
    });
    return Array.from(result_set);
  }

4. 快乐数

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

  1. 数据加密:在计算机领域中,快乐数算法可以用于数据加密,比如在密码学中,对于一些对称加密算法,我们需要对数据进行循环多次加密操作,而为了增加加密后的数据安全性,我们可以使用快乐数算法来作为循环加密的次数,这样可以保证数据循环加密的次数足够多,且循环次数是不可预测的,从而增加数据的安全性。
  2. 人工智能:在人工智能领域中,快乐数算法可以应用于模型训练的过程中。通常情况下,我们需要对模型进行多次训练来优化模型效果,而为了防止模型出现过拟合的情况,我们可以使用快乐数算法来作为模型的训练次数,这样可以避免训练次数过多或过少导致的过拟合或欠拟合问题。
  3. 数字游戏:快乐数算法也可以用于数学游戏中,比如寻找快乐数的过程就可以作为数学游戏的一个环节,增加游戏的趣味性和挑战性。

总之,快乐数算法不仅是一个有趣的数学问题,还具有广泛的应用价值,涵盖了计算机科学、密码学、人工智能、数学教育等多个领域,具有很高的实用性和研究价值。
算法逻辑
输入:19
输出:true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1

/**
* @param {number} n
* @return {boolean}
*/

let getSum = function(n) {
  let sum = 0;
  while (n) {
    sum += (n % 10) * (n % 10);
    n =  Math.floor(n/10);
  }
  return sum;
}
var isHappy = function(n) {
  let set = new Set();
  while (1) {
    let sum = getSum(n);
    // Set() 里的数是惟一的
    if (sum === 1) {
      return true;
    }
    // 如果在循环中某个值重复出现,说明此时陷入死循环,也就说明这个值不是快乐数
    if (set.has(sum)) {
      return false;
    } else {
      set.add(sum);
    }
    n = sum;
  }
};

5. 两数之和

算法应用:

  1. 零钱兑换:例如在超市购物时,需要找零。将可用的货币面值存入数组中,然后通过两数之和算法来找到最少需要哪些面值的零钱。
  2. 搜索平台:例如在网上购物时,输入关键词进行搜索。搜索平台可通过两数之和算法快速找到最匹配的商品和广告,提高用户的购物体验和广告主的曝光量。
  3. 游戏开发:例如在动作游戏中,需要计算出角色与敌人之间的碰撞。两数之和算法可以在很短的时间内找到所有可能的敌人,提高游戏的性能和体验。
  4. 数据库查询:例如在数据分析中,需要对数据进行排序、查找等操作。两数之和算法可以在数据库中快速找到匹配的索引和数据。

算法逻辑:
20220711202638.png
20230220223536.png

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    let map = new Map();// 创建一个 Map 对象
    for (let i = 0; i < nums.length; i++) {
        if (map.has(target - nums[i])) {// 如果找到了相加等于 target 的数
            return [map.get(target - nums[i]), i]// 返回相应的下标
        }
        map.set(nums[i],i);// 否则将当前数及下标加入 Map 中
    }
    return [];// 如果没有找到,返回一个空数组
};

模式识别

  1. 为什么会想到用哈希表
  • 当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候
  1. 哈希表为什么用map
  • 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
  • set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。
  1. 本题map是用来存什么的
  • map目的用来存放我们访问过的元素,因为遍历数组的时候,需要记录我们之前遍历过哪些元素和对应的下标,这样才能找到与当前元素相匹配的
  1. map中的key和value用来存什么的
  • 所以 map中的存储结构为 {key:数据元素,value:数组元素对应的下标}

6. 四数相加II

算法应用:

算法逻辑:

// 定义函数 fourSumCount,接收四个数组作为参数
var fourSumCount = function(nums1, nums2, nums3, nums4) {
    // 定义一个 Map 对象 twosumMap,用于记录 nums1 和 nums2 中各个数字的和出现的次数
    let twosumMap = new Map();
    // 定义计数器 count,用于记录满足条件的四元组的个数
    let count = 0;
    // 遍历 nums1 和 nums2 中的所有数字,将它们的和加入到 twosumMap 中
    for (let item1 of nums1) {
        for (let item2 of nums2) {
            // 计算两个数字的和
            let sum = item1 + item2;
            // 将和出现的次数记录到 twosumMap 中
            twosumMap.set(sum, (twosumMap.get(sum) || 0) + 1);
        }
    }
    // 遍历 nums3 和 nums4 中的所有数字,对于每个数字,判断 twosumMap 中是否存在一个数字,使得这两个数字的和等于 0
    for (let item3 of nums3) {
        for (let item4 of nums4) {
            // 计算两个数字的和
            let sum = item3 + item4;
            // 如果 twosumMap 中存在一个数字,使得这两个数字的和等于 0,则更新计数器 count 的值
            if (twosumMap.has(0 - sum)) {
                count += (twosumMap.get(0 - sum) || 0);
            }
        }
    }
    // 返回计数器 count 的值
    return count;
};

7. 赎金信

算法应用

算法逻辑
因为题目所只有小写字母,那可以采用空间换取时间的哈希策略, 用一个长度为26的数组还记录magazine里字母出现的次数。
然后再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母。
依然是数组在哈希法中的应用。

/**
 * @param {string} ransomNote
 * @param {string} magazine
 * @return {boolean}
 */
var canConstruct = function(ransomNote, magazine) {
  //把magazine放入哈希表中
  let a = new Array(26).fill(0);
  if (ransomNote.length > magazine.length) {
    return false;
  }
  // 通过recode数据记录 magazine里各个字符出现次数
  for (let i = 0; i < magazine.length; i++) {
    a[magazine[i].charCodeAt() - 'a'.charCodeAt()]++;
  }
  // 通过recode数据记录 magazine里各个字符出现次数
  for (let j = 0; j < ransomNote.length; j++) {
    a[ransomNote[j].charCodeAt() - 'a'.charCodeAt()]--;
  }
  // 如果小于零说明ransomNote里出现的字符,magazine没有
  for (let k = 0; k < a.length; k++) {
    if (a[k] < 0) {
      return false;
    }
  }
  return true;
};

8. 三数之和

算法应用

  1. 股票买卖:假设你想要在股市上进行交易,你可以使用三数之和算法来找到符合条件的股票组合,使得它们的总价格达到目标值。
  2. 支付平衡:在一次团购或分摊费用的场合,如果有多个人需要平分费用,你可以使用三数之和算法来找到符合条件的费用组合,使得每个人需要支付的金额尽量接近。
  3. 物品组合:在某些情况下,你可能需要从一堆物品中挑选出一组符合条件的物品组合。例如,在购物时,你可以使用三数之和算法来找到一组物品组合,使得它们的总价格达到你的预算。
  4. 游戏升级:在一些游戏中,你需要收集特定数量的物品才能升级。你可以使用三数之和算法来找到符合条件的物品组合,使得它们的总数量达到升级的要求。

总之,三数之和算法可以在生活中的各种场景中应用,帮助我们快速找到符合条件的组合。
算法逻辑
image.png

// 定义一个函数 twoSum,传入参数 nums 数组、起始位置 start 和目标值 target
function twoSum(nums, start, target) {
  // 定义指针变量 lo 和 hi,分别指向 nums 数组的起始和结尾
  let lo = start, hi = nums.length - 1;
  // 定义结果数组 result,用于存储符合条件的元素组合
  let result = [];
  // 当指针 lo 小于 hi 时,循环判断
  while (lo < hi) {
    // 计算指针所指向的两个元素之和 sum
    let sum = nums[lo] + nums[hi];
    // 定义变量 left 和 right,分别记录指针 lo 和 hi 指向的元素
    let left = nums[lo], right = nums[hi];
    // 如果 sum 小于目标值 target,则将 lo 指针往右移一位
    if (sum < target) {
      lo++;
    } 
    // 如果 sum 大于目标值 target,则将 hi 指针往左移一位
    else if (sum > target) {
      hi--;
    } 
    // 如果 sum 等于目标值 target,则将符合条件的元素组合放入 result 数组中,
    // 并且将指针 lo 和 hi 往中间靠拢,直到找到不同于 left 和 right 的元素
    else {
      result.push([nums[lo], nums[hi]]);
      while (lo < hi && nums[lo] === left) { lo++; }
      while (lo < hi && nums[hi] === right) { hi--; }
    }
  }
  // 返回结果数组 result
  return result;
}

// 定义一个函数 threeSum,传入参数 nums 数组
var threeSum = function(nums) {
  // 对 nums 数组进行升序排列
  nums.sort((a, b) => a - b);
  // 定义结果数组 result,用于存储符合条件的元素组合
  let result = [];
  // 循环遍历 nums 数组,i 从 0 开始,到 nums.length - 2 结束
  for (let i = 0; i < nums.length - 2; i++) {
    // 如果 i 大于 0 并且 nums[i] 等于 nums[i - 1],则跳过本次循环
    // (因为已经找到过以 nums[i] 为第一个元素的符合条件的元素组合了)
    if (i > 0 && nums[i] === nums[i - 1]) {
      continue;
    }
    // 调用函数 twoSum,查找 nums 数组中从 i + 1 开始的元素中,能与 -nums[i] 相加等于 0 的元素组合
    let tuples = twoSum(nums, i + 1, -nums[i]);
    // 遍历元素组合数组 tuples
    for (let tuple of tuples) {
      // 将符合条件的元素组合放入 result 数组中
      result.push([nums[i], ...tuple]);
    }
  }
  // 返回
  return result;
}

9. 四数之和

算法应用:

  1. 金融风险控制:在金融领域中,四数相加算法可以用来计算一系列股票、债券、证券等金融产品的收益和风险,帮助投资者做出决策。
  2. 地震预报:在地震学领域中,四数相加算法可以用来预测地震发生的时间、强度、地点等参数,为应急救援提供指导。
  3. 图像处理:在计算机视觉领域中,四数相加算法可以用来进行图像识别、匹配、修复等操作,帮助人们更好地处理图像信息。
  4. 机器学习:在人工智能领域中,四数相加算法可以用来训练和优化深度神经网络等模型,提高机器学习算法的准确度和效率。

总之,四数相加算法是一种较为高级的算法,它在很多高科技领域中得到应用,为人们提供了更加高效和准确的分析和决策工具。
未命名绘图.png
算法逻辑

var fourSum = function(nums, target) {
  // 将数组按升序排列
  nums.sort((a, b) => a - b);
  const res = [];
  // 如果数组长度小于4,则无法构成4个数的和,返回空结果
  if (nums.length < 4) return res;
  const len = nums.length;
  // 遍历数组中可能成为第一个数的位置i
  for (let i = 0; i < len - 3; i++) {
    // 如果当前i位置的数和后面3个最小的数的和都大于target,退出循环
    if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) break;
    // 如果当前i位置的数和前一个数相同,则跳过
    if (i > 0 && nums[i] === nums[i - 1]) continue;
    // 遍历数组中可能成为第二个数的位置j
    for (let j = i + 1; j < len - 2; j++) {
      // 如果当前i位置的数、j位置的数和后面2个最小的数的和都大于target,退出循环
      if (nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) break;
      // 如果当前j位置的数和前一个数相同,则跳过
      if (j > i + 1 && nums[j] === nums[j - 1]) continue;
      let left = j + 1, right = len - 1;
      // 在i、j之后的数组区间中,寻找两个数left、right,使得四个数的和为target
      while (left < right) {
        const sum = nums[i] + nums[j] + nums[left] + nums[right];
        // 如果四个数的和等于target
        if (sum === target) {
          // 将四个数加入结果数组中
          res.push([nums[i], nums[j], nums[left], nums[right]]);
          // 左指针右移,直到找到下一个与当前left指向的数不相等的数
          while (left < right && nums[left] === nums[left + 1]) left++;
          // 右指针左移,直到找到下一个与当前right指向的数不相等的数
          while (left < right && nums[right] === nums[right - 1]) right--;
          // 左指针和右指针分别移动到下一个数,继续寻找满足条件的两个数
          left++;
          right--;
        } else if (sum < target) {
          // 如果四个数的和小于target,则增大left指向的数,使得sum增大
          left++;
        } else {
          // 如果四个数的和大于target,则减小right指向的数,使得sum减小
          right--;
        }
      }
    }
  }
  return res;
};

10. 总结篇

什么时候适合使用哈希表?
一般来说哈希表都是用来快速判断一个元素是否出现集合里

如何选择哈希结构?
数组:元素范围有限,例如只包含小写字母,数值在0~1000,如果元素很少,而哈希值太大会造成内存空间的浪费
242.有效的字母异位词383.赎金信
set(集合):元素范围不设限制,但元素只能存储一个key
349. 两个数组的交集202.快乐数
map(映射):可以存储<key, value>,如果既要保存元素,又要保存下标就可以用map
1.两数之和

代码总结
用解构合并数组:

result.push([nums[i], ...tuple]);

用map和Set去重

arr = Array.from(new Set(arr.map(JSON.stringify)), JSON.parse)