尚未讲过
字数 3148 2025-12-15 05:36:00

好的,我随机挑选一个尚未讲过的哈希算法相关题目,为你进行细致准确的讲解。

哈希算法题目:键盘行

题目描述

给你一个字符串数组 words,要求你找出并返回那些所有字母都在美式键盘的同一行上的单词。你可以按任意顺序返回答案。

美式键盘的字母行如下:

  • 第一行:"qwertyuiop"
  • 第二行:"asdfghjkl"
  • 第三行:"zxcvbnm"

示例 1:
输入:words = ["Hello","Alaska","Dad","Peace"]
输出:["Alaska","Dad"]
解释:

  • “Hello” 中的 ‘h’ 和 ‘e’ 在第二行,但 ‘l’ 和 ‘o’ 在第一行,不符合。
  • “Alaska” 所有字母都在第二行。
  • “Dad” 所有字母都在第二行(注意大小写不敏感,’D’ 视为 ‘d’)。
  • “Peace” 中的 ‘p’ 在第一行,其他字母在第二行,不符合。

示例 2:
输入:words = ["omk"]
输出:[]

示例 3:
输入:words = ["adsdf","sfd"]
输出:["adsdf","sfd"]


解题过程循序渐进讲解

这个问题非常适合使用哈希表来解决。核心思路是:为每个字母快速查询它属于键盘的哪一行,然后检查一个单词的所有字母是否属于同一行

步骤一:理解需求与建立映射

题目要求判断单词字母是否在同一行。首先,我们需要建立一个从字母到行号的映射关系

  1. 忽略大小写:键盘行定义的是小写字母,所以处理每个字符时,应统一转换为小写(或大写)后再进行判断。
  2. 创建哈希映射:最直接的方式是创建三个字符串,分别代表三行键盘字符。然后,为了快速查询,我们可以创建一个 Map(字典),键(key)是字母,值(value)是该字母所在的行号(例如 0, 1, 2)。
  3. 填充映射
    # 伪代码示例
    row_map = {}  # 初始化一个空的哈希映射
    
    # 第一行字母,行号设为 0
    for char in qwertyuiop:
        row_map[char] = 0
    
    # 第二行字母,行号设为 1
    for char in asdfghjkl:
        row_map[char] = 1
    
    # 第三行字母,行号设为 2
    for char in zxcvbnm:
        row_map[char] = 2
    
    现在,row_map[‘a’] 会返回 1row_map[‘q’] 会返回 0。查询的时间复杂度是 O(1)。

步骤二:设计单词检查逻辑

对于一个给定的单词(如 “Alaska”),如何判断其所有字母是否在同一行?

  1. 获取首个字母的基准行号:将单词的第一个字母(转换为小写)作为基准,从 row_map 中查出它的行号。
    • 例如 “Alaska” -> 第一个字母 ‘a’(小写) -> row_map[‘a’] -> 1
  2. 遍历后续字母:从单词的第二个字母开始,依次将每个字母(转换为小写)在 row_map 中查询其行号。
  3. 进行比较:将当前字母的行号与基准行号进行比较。
    • 如果所有后续字母的行号都与基准行号相等,那么这个单词满足条件。
    • 如果有任何一个字母的行号与基准行号不相等,那么这个单词不满足条件,可以立即停止检查。
  4. 逻辑流程图
    开始检查单词
        |
    将单词首字母小写,并查询其行号 -> 设为 `base_row`
        |
    遍历单词的剩余字母:
        |
        将当前字母小写,查询其行号 -> 设为 `current_row`
        |
        ┌── current_row == base_row ? ──┐
        │                               │
        │ 是 (继续)                   否 (中断)    │
        ↓                               ↓
    检查下一个字母              将此单词标记为不合格
        |                               |
        ↓                               ↓
    (所有字母检查完毕)                (结束)
        |
        ↓
    将此单词标记为合格
    

步骤三:整合算法与处理边界

现在,我们将步骤一和步骤二整合,并处理整个字符串数组。

  1. 预处理:在算法开始前,先构建好 row_map。这是一个一次性的操作。
  2. 主循环:遍历输入的 words 数组,对每个单词执行步骤二的检查逻辑。
  3. 收集结果:创建一个结果列表 result。如果一个单词通过检查,就将其原样(保持原有大小写)添加到 result 中。
  4. 边界情况考虑
    • 空单词:题目通常不会给,但如果有,可以认为它 vacuously true(所有字母都在同一行)或直接跳过。为了安全,可以视为满足条件(因为不存在不同行的字母)。
    • 单个字母的单词:它显然满足条件(只有一个字母,当然“所有”字母都在同一行)。

步骤四:代码实现与示例推演

让我们用示例一 ["Hello","Alaska","Dad","Peace"] 来推演。

  • 构建 row_map:(略,如前所述)
  • 检查 “Hello”
    • 首字母 ‘h’ -> row_map[‘h’] -> 1 (第二行)。
    • 检查 ‘e’ -> row_map[‘e’] -> 1 (相同,继续)。
    • 检查 ‘l’ -> row_map[‘l’] -> 0 (第一行) -> 与基准 1 不同! -> 不合格,跳过。
  • 检查 “Alaska”
    • 首字母 ‘a’ -> row_map[‘a’] -> 1
    • 检查 ‘l’ -> row_map[‘l’] -> 0 -> 不同! -> 不合格,跳过。等等,这里好像不对?
    • 注意:我们需要将整个单词统一转为小写后再比较!“Alaska” 转为小写是 “alaska”
    • 重新检查 “alaska”
      • 首字母 ‘a’ -> 行号 1
      • 检查 ‘l’ -> 行号 0 -> 不同 -> 不合格。是的,我最初举的例子 “Alaska” 中其实有个 ‘l’ 在第一行,所以它并不合格。看来我记忆有误,我们以示例的正确答案为准,“Alaska” 应该是合格的。
    • 纠正:让我们严格按示例来。“Alaska” 字母为 A, l, a, s, k, a。小写后 a, l, a, s, k, a
      • 首字母 ‘a’ -> 行号 1
      • 检查 ‘l’ -> 行号 0 -> 不同!等一下,根据键盘布局,字母 ‘l’ 在第二行 asdfghjkl 里!row_map[‘l’] 应该是 1,不是 0。我之前的键盘行字符串写错了。
      • 重要修正
        • 第二行是 “asdfghjkl”,包含 ‘l’
        • 第一行是 “qwertyuiop”,不包含 ‘l’
      • 所以 row_map[‘l’] = 1
      • 继续检查 ‘a’ -> 1‘s’ -> 1‘k’ -> 1‘a’ -> 1全部相同。因此 “Alaska” 合格
  • 检查 “Dad”
    • 小写 “dad”。首字母 ‘d’ -> row_map[‘d’] -> 1
    • 检查 ‘a’ -> 1‘d’ -> 1。合格。
  • 检查 “Peace”
    • 小写 “peace”。首字母 ‘p’ -> row_map[‘p’] -> 0 (第一行)。
    • 检查 ‘e’ -> row_map[‘e’] -> 1 (第二行) -> 不同!不合格。

最终结果[“Alaska”, “Dad”],与题目示例一致。

步骤五:复杂度分析

  • 时间复杂度O(N * L),其中 N 是单词数组 words 的长度,L 是单词的平均长度。
    • 构建 row_mapO(1),因为键盘字母总数是固定的 26。
    • 对于每个单词,我们至多遍历其所有字符一次。哈希表查询操作是 O(1)
  • 空间复杂度O(1)O(26)。用于存储 row_map 的空间是固定大小的,与输入无关。结果列表 result 使用的空间是 O(N),但这是输出所需的,通常不计入额外的空间复杂度。

总结

这道题巧妙利用了哈希表快速查找的特性。核心步骤是:

  1. 构建静态映射:用哈希表建立 26 个字母到其键盘行号的映射。
  2. 逐单词判断:对每个单词,先获取其首字母的行号作为基准,然后遍历后续字母,利用哈希表快速获取每个字母的行号并与基准比较。
  3. 大小写处理:在查询前统一将字母转换为小写,确保映射正确。

这种方法思路清晰,效率很高,是解决此类“归类判断”问题的典型哈希表应用。

好的,我随机挑选一个 尚未讲过 的哈希算法相关题目,为你进行细致准确的讲解。 哈希算法题目:键盘行 题目描述 给你一个字符串数组 words ,要求你找出并返回那些 所有字母都在美式键盘的同一行上 的单词。你可以按任意顺序返回答案。 美式键盘的字母行如下: 第一行: "qwertyuiop" 第二行: "asdfghjkl" 第三行: "zxcvbnm" 示例 1: 输入: words = ["Hello","Alaska","Dad","Peace"] 输出: ["Alaska","Dad"] 解释: “Hello” 中的 ‘h’ 和 ‘e’ 在第二行,但 ‘l’ 和 ‘o’ 在第一行,不符合。 “Alaska” 所有字母都在第二行。 “Dad” 所有字母都在第二行(注意大小写不敏感,’D’ 视为 ‘d’)。 “Peace” 中的 ‘p’ 在第一行,其他字母在第二行,不符合。 示例 2: 输入: words = ["omk"] 输出: [] 示例 3: 输入: words = ["adsdf","sfd"] 输出: ["adsdf","sfd"] 解题过程循序渐进讲解 这个问题非常适合使用哈希表来解决。核心思路是: 为每个字母快速查询它属于键盘的哪一行,然后检查一个单词的所有字母是否属于同一行 。 步骤一:理解需求与建立映射 题目要求判断单词字母是否在同一行。首先,我们需要建立一个 从字母到行号的映射关系 。 忽略大小写 :键盘行定义的是小写字母,所以处理每个字符时,应统一转换为小写(或大写)后再进行判断。 创建哈希映射 :最直接的方式是创建三个字符串,分别代表三行键盘字符。然后,为了快速查询,我们可以创建一个 Map (字典),键( key )是字母,值( value )是该字母所在的行号(例如 0, 1, 2)。 填充映射 : 现在, row_map[‘a’] 会返回 1 , row_map[‘q’] 会返回 0 。查询的时间复杂度是 O(1)。 步骤二:设计单词检查逻辑 对于一个给定的单词(如 “Alaska”),如何判断其所有字母是否在同一行? 获取首个字母的基准行号 :将单词的第一个字母(转换为小写)作为基准,从 row_map 中查出它的行号。 例如 “Alaska” -> 第一个字母 ‘a’(小写) -> row_map[‘a’] -> 1 。 遍历后续字母 :从单词的第二个字母开始,依次将每个字母(转换为小写)在 row_map 中查询其行号。 进行比较 :将当前字母的行号与 基准行号 进行比较。 如果 所有 后续字母的行号都与基准行号 相等 ,那么这个单词满足条件。 如果 有任何 一个字母的行号与基准行号 不相等 ,那么这个单词不满足条件,可以立即停止检查。 逻辑流程图 : 步骤三:整合算法与处理边界 现在,我们将步骤一和步骤二整合,并处理整个字符串数组。 预处理 :在算法开始前,先构建好 row_map 。这是一个一次性的操作。 主循环 :遍历输入的 words 数组,对每个单词执行步骤二的检查逻辑。 收集结果 :创建一个结果列表 result 。如果一个单词通过检查,就将其 原样 (保持原有大小写)添加到 result 中。 边界情况考虑 : 空单词 :题目通常不会给,但如果有,可以认为它 vacuously true(所有字母都在同一行)或直接跳过。为了安全,可以视为满足条件(因为不存在不同行的字母)。 单个字母的单词 :它显然满足条件(只有一个字母,当然“所有”字母都在同一行)。 步骤四:代码实现与示例推演 让我们用示例一 ["Hello","Alaska","Dad","Peace"] 来推演。 构建 row_map :(略,如前所述) 检查 “Hello” : 首字母 ‘h’ -> row_map[‘h’] -> 1 (第二行)。 检查 ‘e’ -> row_map[‘e’] -> 1 (相同,继续)。 检查 ‘l’ -> row_map[‘l’] -> 0 (第一行) -> 与基准 1 不同! -> 不合格,跳过。 检查 “Alaska” : 首字母 ‘a’ -> row_map[‘a’] -> 1 。 检查 ‘l’ -> row_map[‘l’] -> 0 -> 不同! -> 不合格,跳过。等等,这里好像不对? 注意 :我们需要将整个单词统一转为小写后再比较! “Alaska” 转为小写是 “alaska” 。 重新检查 “alaska” : 首字母 ‘a’ -> 行号 1 。 检查 ‘l’ -> 行号 0 -> 不同 -> 不合格。是的,我最初举的例子 “Alaska” 中其实有个 ‘l’ 在第一行,所以它并不合格。看来我记忆有误,我们以示例的正确答案为准, “Alaska” 应该是合格的。 纠正 :让我们严格按示例来。 “Alaska” 字母为 A, l, a, s, k, a 。小写后 a, l, a, s, k, a 。 首字母 ‘a’ -> 行号 1 。 检查 ‘l’ -> 行号 0 -> 不同 !等一下,根据键盘布局,字母 ‘l’ 在第二行 asdfghjkl 里! row_map[‘l’] 应该是 1 ,不是 0 。我之前的键盘行字符串写错了。 重要修正 : 第二行是 “asdfghjkl” ,包含 ‘l’ 。 第一行是 “qwertyuiop” ,不包含 ‘l’ 。 所以 row_map[‘l’] = 1 。 继续检查 ‘a’ -> 1 , ‘s’ -> 1 , ‘k’ -> 1 , ‘a’ -> 1 。 全部相同 。因此 “Alaska” 合格 。 检查 “Dad” : 小写 “dad” 。首字母 ‘d’ -> row_map[‘d’] -> 1 。 检查 ‘a’ -> 1 , ‘d’ -> 1 。合格。 检查 “Peace” : 小写 “peace” 。首字母 ‘p’ -> row_map[‘p’] -> 0 (第一行)。 检查 ‘e’ -> row_map[‘e’] -> 1 (第二行) -> 不同 !不合格。 最终结果 : [“Alaska”, “Dad”] ,与题目示例一致。 步骤五:复杂度分析 时间复杂度 : O(N * L) ,其中 N 是单词数组 words 的长度, L 是单词的平均长度。 构建 row_map 是 O(1) ,因为键盘字母总数是固定的 26。 对于每个单词,我们至多遍历其所有字符一次。哈希表查询操作是 O(1) 。 空间复杂度 : O(1) 或 O(26) 。用于存储 row_map 的空间是固定大小的,与输入无关。结果列表 result 使用的空间是 O(N) ,但这是输出所需的,通常不计入额外的空间复杂度。 总结 这道题巧妙利用了哈希表 快速查找 的特性。核心步骤是: 构建静态映射 :用哈希表建立 26 个字母到其键盘行号的映射。 逐单词判断 :对每个单词,先获取其首字母的行号作为基准,然后遍历后续字母,利用哈希表快速获取每个字母的行号并与基准比较。 大小写处理 :在查询前统一将字母转换为小写,确保映射正确。 这种方法思路清晰,效率很高,是解决此类“归类判断”问题的典型哈希表应用。