Leetcode尊享面试100题【Part3 哈希】【题目+分析+答案】

在这里插入图片描述

3 哈希

哈希表(Hash Table),也称为散列表,是一种常用的数据结构,用于实现键-值(key-value)对的高效存储和查找。它通过将键映射到一个固定大小的数组(称为哈希表)中的索引位置来实现快速的访问。

哈希表的基本思想是利用哈希函数(Hash Function)将键转换成数组索引,使得每个键都可以在数组中找到唯一的位置。当需要插入或查找一个键时,通过哈希函数计算出相应的索引,然后在该索引位置上进行操作

3.1 找出变位映射

题目描述

给定两个列表 A and B,并且 BA 的变位(即 B 是由 A 中的元素随机排列后组成的新列表)。

我们希望找出一个从 AB 的索引映射 P 。一个映射 P[i] = j 指的是列表 A 中的第 i 个元素出现于列表 B 中的第 j 个元素上。

列表 AB 可能出现重复元素。如果有多于一种答案,输出任意一种。

例如,给定

A = [12, 28, 46, 32, 50]
B = [50, 12, 32, 46, 28]

需要返回

[1, 4, 3, 2, 0]
P[0] = 1 ,因为 A 中的第 0 个元素出现于 B[1],而且 P[1] = 4 因为 A 中第 1 个元素出现于 B[4],以此类推。

分思路析
A=[12,28,46],B=[46,12,28] 为例。我们想知道 12,28,46B 中的位置,如果有一个字典(哈希表)D = {12: 1, 28: 2, 46: 0},那么这个问题可以很轻松的被解决。

实现代码

1.遍历+查询

class Solution:
    def anagramMappings(self, A: List[int], B: List[int]) -> List[int]:
        n = len(B)
        # defaultdict是一个类型于字典的数据结构,它是collections模块提供的一个类
# 当我们访问字典中不存在的键时,defaultdict会根据指定的默认值类型自动创建并初始化键,
#而不会抛出KeyError异常。这使得我们可以更方便地处理某些特定的字典操作,例如计数、分组等

        num_ids = defaultdict(list)
        for index, value in enumerate(B):
            num_ids[value].append(index)
            # num_ids为 value:index
        
        res = []
        for x in A:
            # 遍历A的每个元素, 即为num_ids的索引
        
        	# pop()用于删除指定键对应的键值对,并返回该键对应的值
            tmp = num_ids[x].pop(0) #返回x对应B中的索引
            res.append(tmp)
        return res

2.简单写法,但时间复杂度较高
map的用法

map(function, iterable)
其中,function 是要应用的函数,iterable 是一个或多个可迭代对象(如列表、元组等)
def square(x):
    return x ** 2

numbers = [1, 2, 3, 4, 5]
squared_numbers = map(square, numbers)

print(list(squared_numbers))  # 输出: [1, 4, 9, 16, 25]
def add(x, y):
    return x + y

numbers1 = [1, 2, 3]
numbers2 = [10, 20, 30]
result = map(add, numbers1, numbers2)

print(list(result))  # 输出: [11, 22, 33]
class Solution:
    def anagramMappings(self, A: List[int], B: List[int]) -> List[int]:
    #list.index()用于在列表中查找指定元素的索引位置,并返回第一次出现该元素的索引值。
        return list(map(B.index, A))

3.2 回文排列

题目描述
给定一个字符串,判断该字符串中是否可以通过重新排列组合,形成一个回文字符串。

示例 1:
输入: "code"
输出: false

示例 2:
输入: "aab"
输出: true

示例 3:
输入: "carerac"
输出: true

分思路析
哈希 字典统计 每个字母出现次数

且最多允许一个字母出现奇数次

实现代码

class Solution:
    def canPermutePalindrome(self, s: str) -> bool:
        char_freq = defaultdict(int)
        #d = defaultdict(int)  print(d['key'])   # 输出: 0
        for c in s:
            char_freq[c] += 1
        cnt = 0
        for c, freq in char_freq.items():
            if freq % 2 == 1:
                cnt += 1
                if cnt > 1:         #只允许一个字母出现奇数次
                    return False
        return True
class Solution:
    def canPermutePalindrome(self, s: str) -> bool:
        dic = {}
        for i in s:
            dic[i] = dic.get(i)+1
            #dict.get(i, default) 是针对字典(dict)对象的方法调用,用于获取指定键 i 的值。
            # 如果键 i 存在于字典中,则返回与该键关联的值;如果键 i 不存在于字典中,则返回预设值 default。
            # 没有default 返回None
        count = 0
        for i in dic.values():
            if i%2==1:
                count+=1
        return count<2

3.3 句子相似性

题目描述
我们可以将一个句子表示为一个单词数组,例如,句子 "I am happy with leetcode" 可以表示为 arr = ["I","am",happy","with","leetcode"]

给定两个句子 sentence1sentence2 分别表示为一个字符串数组,并给定一个字符串对 similarPairs ,其中 similarPairs[i] = [xi, yi] 表示两个单词 xi and yi 是相似的。

如果 sentence1 sentence2 相似则返回 true ,如果不相似则返回 false

两个句子是相似的,如果:
它们具有 相同的长度 (即相同的字数)
sentence1[i] sentence2[i] 是相似的
请注意,一个词总是与它自己相似,也请注意,相似关系是不可传递的。例如,如果单词 a 和 b 是相似的,单词 b 和 c 也是相似的,那么 a 和 c 不一定相似 。

示例 1:
输入: sentence1 = ["great","acting","skills"], sentence2 = ["fine","drama","talent"], 
similarPairs = [["great","fine"],["drama","acting"],["skills","talent"]]
输出: true
解释: 这两个句子长度相同,每个单词都相似。

示例 2:
输入: sentence1 = ["great"], sentence2 = ["great"], similarPairs = []
输出: true
解释: 一个单词和它本身相似。

示例 3:
输入: sentence1 = ["great"], sentence2 = ["doubleplus","good"], 
similarPairs = [["great","doubleplus"]]
输出: false
解释: 因为它们长度不同,所以返回false。

分思路析
哈希 字典统计 每个字母出现次数;且最多允许一个字母出现奇数次

实现代码

class Solution:
    def areSentencesSimilar(self, sentence1: List[str], sentence2: List[str], similarPairs: List[List[str]]) -> bool:
        n1 = len(sentence1)
        n2 = len(sentence2)
        #先判断sentence1和sentence2长度是否相等,若不同返回False
        if n1 != n2:
            return False
        
        dic = defaultdict(set)
        for x, y in similarPairs:
            dic[x].add(y)
            dic[y].add(x)
        
        flag = True
        #zip() 函数用于将多个可迭代对象(例如列表、元组等)中对应位置的元素打包成一个元组
        for x, y in zip(sentence1, sentence2):
            if x == y:
                continue
            if x in dic[y]:
                continue
            flag = False
            break
        return flag
class Solution:
    def areSentencesSimilar(self, sentence1, sentence2, similarPairs):
        if len(sentence1) != len(sentence2):
            return False
        #将similarPairs转化为hashset,可以加快匹配查找的速度    
        sim = set(map(tuple, similarPairs))
        #map(tuple, similarPairs)函数的作用是将similarPairs列表中的每个子列表转换为元组,set去重
        #{(a, b), (c, d)}
        for i in range(len(sentence1)):
            s1, s2 = sentence1[i], sentence2[i]
            if s1 == s2 or (s1, s2) in sim or (s2, s1) in sim:
                continue
            return False
        return True

3.4 单行键盘

题目描述
我们定制了一款特殊的键盘,所有的键都 排列在一行上 。
给定一个长度为 26 的字符串 keyboard ,来表示键盘的布局(索引从 025 )。一开始,你的手指在索引 0 处。要输入一个字符,你必须把你的手指移动到所需字符的索引处。手指从索引 i 移动到索引 j 所需要的时间是 |i - j|

您需要输入一个字符串 word 。写一个函数来计算用一个手指输入需要多少时间。

示例 1:
输入:keyboard = "abcdefghijklmnopqrstuvwxyz", word = "cba"
输出:4
解释:从 0 号键移动到 2 号键来输出 'c',又移动到 1 号键来输出 'b',接着移动到 0 号键来输出 'a'。
总用时 = 2 + 1 + 1 = 4.

示例 2:
输入:keyboard = "pqrstuvwxyzabcdefghijklmno", word = "leetcode"
输出:73

分思路析
首先用哈希表记录字符的下标,再遍历word加上差值即可

实现代码

class Solution:
    def calculateTime(self, keyboard: str, word: str) -> int:
        char_map = dict()
        for i, char_s in enumerate(keyboard):
            char_map[char_s] = i

        cur_pos, step = 0, 0
        for char_s in word:
            step += abs(cur_pos - char_map[char_s])
            cur_pos = char_map[char_s]
        
        return step

3.5 移位字符串分组

题目描述
给定一个字符串,对该字符串可以进行 “移位” 的操作,也就是将字符串中每个字母都变为其在字母表中后续的字母,比如:"abc" -> "bcd"。这样,我们可以持续进行 “移位” 操作,从而生成如下移位序列:

"abc" -> "bcd" -> ... -> "xyz"

给定一个包含仅小写字母字符串的列表,将该列表中所有满足 “移位” 操作规律的组合进行分组并返回。

示例:
输入:["abc", "bcd", "acef", "xyz", "az", "ba", "a", "z"]
输出:
[
  ["abc","bcd","xyz"],
  ["az","ba"],
  ["acef"],
  ["a","z"]
]
解释:可以认为字母表首尾相接,所以 'z' 的后续为 'a',所以 ["az","ba"] 也满足 “移位” 操作规律。

分思路析
对每个字符串进行“移位”操作,把他们都转换成以a开头的字符串,然后用哈希表进行分类

实现代码

class Solution:
    def groupStrings(self, strings: List[str]) -> List[List[str]]:
        mode_list = defaultdict(list)
        for s in strings:
            if s[0] == 'a':
                mode_list[s].append(s)
            else:
                tmp = list(s) #转化成一种模式 从'a****'
                for i in range(len(s)):
                    tmp[i] = chr( (ord(tmp[i]) - ord(s[0]) + 26) % 26 + ord('a') )
                tmp = ''.join(tmp)
                mode_list[tmp].append(s)
        res = []
        for mode, sublist in mode_list.items():
            res.append(sublist)               
        return res

3.6 最大唯一数

题目描述
给你一个整数数组 A,请找出并返回在该数组中仅出现一次的最大整数。
如果不存在这个只出现一次的整数,则返回 -1。

示例 1:
输入:[5,7,3,9,4,9,8,3,1]
输出:8
解释: 
数组中最大的整数是 9,但它在数组中重复出现了。而第二大的整数是 8,它只出现了一次,所以答案是 8。

示例 2:
输入:[9,9,8,8]
输出:-1
解释: 
数组中不存在仅出现一次的整数。

分思路析
字典统计 注意不存在的情况

实现代码

class Solution:
    def largestUniqueNumber(self, A: List[int]) -> int:
        digit_freq = defaultdict(int)
        for x in A:
            digit_freq[x] += 1
        rec = []
        for digit, freq in digit_freq.items():
            if freq == 1:
                rec.append(digit)
        if len(rec) == 0:
            return -1
        return max(rec)

3.7 数元素

题目描述
给你一个整数数组 arr, 对于元素 x ,只有当 x + 1 也在数组 arr 里时,才能记为 1 个数。

如果数组 arr 里有重复的数,每个重复的数单独计算。

示例 1:
输入:arr = [1,2,3]
输出:2
解释:12 被计算次数因为 23 在数组 arr 里。

示例 2:
输入:arr = [1,1,3,3,5,5,7,7]
输出:0
解释:所有的数都不算, 因为数组里没有 2468

实现代码

class Solution:
    def countElements(self, arr: List[int]) -> int:
        ##借助哈希字典统计,然后再遍历,查找
        num_cnt = collections.Counter(arr)

        res = 0
        for num, cnt in num_cnt.items():
            if (num + 1) in num_cnt:
                res += cnt
        
        return res
class Solution:
    def countElements(self, arr: List[int]) -> int:
        return sum(arr[i]+1 in set(arr)  for i in range(len(arr)))
class Solution:
   def countElements(self, arr: List[int]) -> int:
       return sum(x + 1 in arr for x in arr)   

3.8 找出所有行中最小公共元素

题目描述
给你一个 m x n 的矩阵 mat,其中每一行的元素均符合 严格递增 。请返回 所有行中的 最小公共元素 。

如果矩阵中没有这样的公共元素,就请返回 -1

示例 1:
输入:mat = [[1,2,3,4,5],[2,4,5,8,10],[3,5,7,9,11],[1,3,5,7,9]]
输出:5

示例 2:
输入:mat = [[1,2,3],[2,3,4],[2,3,5]]
输出: 2

分思路析
有序必二分, 二分法

实现代码
二分查找

class Solution:
    def smallestCommonElement(self, mat: List[List[int]]) -> int:    
        #if not mat:语句用于判断列表mat是否为空
        if not mat:
            return -1
        
        # 二分查找 二分查找算法可以高效地在有序数组中查找目标元素
        def binarySearch(nums, target):
            left = 0
            right = len(nums) - 1
            
			#当左边界小于等于右边界时,进行循环。
            while left <= right:
            	#计算中间元素的索引,使用整数除法以确保结果是整数
            	mid = (left + right) // 2 #向下取整
            	#如果中间元素就是目标元素,则返回 True
                if nums[mid] == target:
                    return True
                #如果中间元素小于目标元素,则在右半部分继续查找,将左边界更新为 mid + 1
                elif nums[mid] < target:
                    left = mid + 1
                #如果中间元素大于目标元素,则在左半部分继续查找,将右边界更新为 mid - 1
                else:
                    right = mid - 1
            return False

        # 获取矩阵中的行数和列数
        rows = len(mat)
        cols = len(mat[0])

        # 遍历第一行的元素
        for num in mat[0]:
            found = True  # 标记当前元素是否在所有行中都存在
            # 遍历除了第一行以外的每一行
            for i in range(1, rows):
                # 使用二分查找在当前行中找到当前元素
                if not binarySearch(mat[i], num):
                    found = False
                    break
            # 如果当前元素在所有行中都存在,则是最小公共元素
            if found:
                return num

        # 没有找到最小公共元素
        return -1

逐行对比最小值

class Solution:
    def smallestCommonElement(self, mat: List[List[int]]) -> int:
        if not mat:
            return -1
        n = len(mat)
        m = len(mat[0])
        counts = collections.defaultdict(int)
        for i in range(m): # 列
            for j in range(n): # 行
                val = mat[j][i]
                if counts[val] == n - 1:
                    return val
                counts[val] += 1
        return -1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大西瓜的科研日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值