Leetcode1:两数之和:中等题 (详情点击链接见原题)
给定一个整数数组
nums
和一个整数目标值target
,请你在该数组中找出 和为目标值target
的那 两个 整数,并返回它们的数组下标
python代码解法
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hash_table = {}
for index, value in enumerate(nums):
if target - value in hash_table:
return [index, hash_table[target - value]]
hash_table[value] = index # 以nums数组的value为key,index为value
return []
Leetcode205:同构字符串:简单题 (详情点击链接见原题)
给定两个字符串
s
和t
,判断它们是否是同构的。
如果s
中的字符可以按某种映射关系替换得到t
,那么这两个字符串是同构的
解题思路
相同的字符只能映射到同一个字符上,不同字符不能映射到同一个字符上
s
和 t
之间是 双射, 满足一一对应,考虑遍历字符串,使用哈希表 s_to_t
,t_to_s
分别记录 s—>t
, t—>s
的映射,当发现任意不满足一对一的关系时返回 False
即可
python代码解法
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
if len(s) != len(t):
return False
s_to_t = {}
t_to_s = {}
for i in range(len(s)):
if s[i] not in s_to_t:
s_to_t[s[i]] = t[i]
if t[i] not in t_to_s:
t_to_s[t[i]] = s[i]
if s_to_t[s[i]] != t[i] or t_to_s[t[i]] != s[i]:
return False
return True
Leetcode290. 单词规律:简单题 (详情点击链接见原题)
给定一种规律
pattern
和一个字符串s
,判断s
是否遵循相同的规律。
这里的 遵循 指完全匹配,例如,pattern
里的每个字母和字符串s
中的每个非空单词之间存在着双向连接的对应规律
python代码解法
class Solution:
def wordPattern(self, pattern: str, s: str) -> bool:
words = s.split()
if len(pattern) != len(words):
return False
s_p = {}
p_s = {}
for char, word in zip(pattern, words):
if (char in p_s and p_s[char] != word) or (word in s_p and s_p[word] != char):
return False
p_s[char] = word
s_p[word] = char
return True
Leetcode349:两个数组的交集:简单题 (详情点击链接见原题)
给定两个数组
nums1
和nums2
,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序
python代码解法1
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
common = {}
for i in nums1:
common[i] = common.get(i, 0) + 1
res = []
for j in nums2:
if j in common and j not in res:
res.append(j)
return res
python代码解法2
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
nums1_lis = [0] * 1001
nums2_lis = [0] * 1001
for i in nums1:
nums1_lis[i] = 1
for j in nums2:
nums2_lis[j] = 1
res = []
for i in range(1001):
if nums2_lis[i] * nums1_lis[i]:
res.append(i)
return res
Leetcode697. 数组的度:简单题 (详情点击链接见原题)
给定一个非空且只包含非负数的整数数组
nums
,数组的 度 的定义是指数组里任一元素出现频数的最大值
python代码解法1
class Solution:
def findShortestSubArray(self, nums: List[int]) -> int:
count_1 = [0] * 50000
count_2 = [0] * 50000
for i in nums:
count_1[i] += 1
max_fre = max(count_1)
left, right = 0, 0
min_len = float('inf')
while right < len(nums):
count_2[nums[right]] += 1
while count_2[nums[right]] == max_fre:
if count_2[nums[left]] == max_fre:
min_len = min(min_len, right - left + 1)
count_2[nums[left]] -= 1
left += 1
right += 1
return min_len
Leetcode383. 赎金信:简单题 (详情点击链接见原题)
给你两个字符串:
ransomNote
和magazine
,判断ransomNote
能不能由magazine
里面的字符构成
python代码解法
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
char_map = [0] * 26
for r in ransomNote:
char_map[ord(r) - ord('a')] += 1
for m in magazine:
char_map[ord(m) - ord('a')] -= 1
for r in ransomNote:
if char_map[ord(r) - ord('a')] > 0:
return False
return True
Leetcode202. 快乐数:中等题 (详情点击链接见原题)
编写一个算法来判断一个数
n
是不是快乐数,如果n
是 快乐数 就返回true
;不是,则返回false
解题思路
题目中说了会无限循环,那么也就是说求和过程中,sum
会重复出现,这对题解很重要,当我们遇到了要快速判断一个元素是否出现集合里的时候就要考虑哈希法了
python代码解法
class Solution:
def bitSum(self, n):
res = 0
while n > 0:
temp = n % 10
res += temp ** 2
n //= 10
return res
def isHappy(self, n: int) -> bool:
hash_map = []
while n:
n = self.bitSum(n)
hash_map.append(n)
if self.bitSum(n) in hash_map:
if n == 1:
return True
else:
return False
附上另一种解题思路,快慢指针法
python代码解法2
class Solution:
def bitSquareSum(self, n):
total = 0
while n > 0:
bit = n % 10
total += bit ** 2
n //= 10
return total
def isHappy(self, n: int) -> bool:
slow, fast = n, n
while self.bitSquareSum(fast) != 1 and self.bitSquareSum(self.bitSquareSum(fast)):
slow = self.bitSquareSum(slow)
fast = self.bitSquareSum(fast)
fast = self.bitSquareSum(fast)
if slow == fast:
return False
return True
Leetcode242. 有效的字母异位词:中等题 (详情点击链接见原题)
利用数组作为映射字母和对应字母的出现次数的映射关系
python代码解法
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
char_map = [0] * 26
if len(s) != len(t):
return False
for index, value in enumerate(s):
char_map[ord(value) - ord('a')] += 1
char_map[ord(t[index]) - ord('a')] -= 1
for j in char_map:
if j != 0:
return False
return True
Leetcode49:字母异位词分组:中等题 (详情点击链接见原题)
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表
python代码解法1
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
hash_table = {}
for st in strs:
key = "".join(sorted(st))
if key not in hash_table:
hash_table[key] = [st]
else:
hash_table[key].append(st)
return list(hash_table.values())
python代码解法2
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
hash_table = {}
for st in strs:
char_map = [0] * 26
for i in range(0, len(st)):
char_map[ord(st[i]) - ord('a')] += 1
key = "-".join(str(i) for i in char_map) # 保证键的唯一性
print(key)
if key not in hash_table:
hash_table[key] = [st]
else:
hash_table[key].append(st)
return list(hash_table.values())
Leetcode2131. 连接两字母单词得到的最长回文串:中等题 (详情点击链接见原题)
给你一个字符串数组
words
。words
中每个元素都是一个包含 两个 小写英文字母的单词
方法1:用二维数组代替哈希
python代码解法
class Solution:
def longestPalindrome(self, words: List[str]) -> int:
char_map = [[0] * 26 for _ in range(26)]
palindromeLen = 0
for word in words:
c1 = ord(word[0]) - ord('a')
c2 = ord(word[1]) - ord('a')
if char_map[c2][c1] > 0:
char_map[c2][c1] -= 1
palindromeLen += 4
continue
char_map[c1][c2] += 1
for i in range(26):
if char_map[i][i] > 0:
palindromeLen += 2
break
return palindromeLen
方法2:类似于两数之和的解题思想
from typing import List
from collections import Counter
class Solution:
def longestPalindrome(self, words: List[str]) -> int:
word_map = Counter()
palindromeLen = 0
for word in words:
word_reverse = word[::-1]
if word_map[word_reverse] > 0:
word_map[word_reverse] -= 1
palindromeLen += 4 # 匹配成功,可以添加到最长回文串的两端
continue
else:
word_map[word] += 1
for word, value in word_map.items():
if word[0] == word[1] and value > 0:
palindromeLen += 2
break
return palindromeLen
Leetcode594. 最长和谐子序列:简单题 (详情点击链接见原题)
和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是
1
。
现在,给你一个整数数组nums
,请你在所有可能的子序列中找到最长的和谐子序列的长度
我们可以通过哈希表记录所有的 nums[i]
的出现次数,然后通过 O(n)
的复杂度找出所有可能的对数(两数差值为 1
)并在所有符合条件的数对所能构成的 和谐子序列 长度中取最大值
python代码解法
from collections import Counter
class Solution:
def findLHS(self, nums: List[int]) -> int:
if len(nums) <= 1:
return 0
hash_map = Counter(nums)
ans = 0
nums.sort()
for i in nums[1:]:
if i - 1 in hash_map:
ans = max(ans, hash_map[i] + hash_map[i - 1])
return ans
Leetcode268. 丢失的数字:简单题 (详情点击链接见原题)
给定一个包含
[0, n]
中n
个数的数组nums
,找出[0, n]
这个范围内没有出现在数组中的那个数
原地哈希
我们可以将 nums
本身作为哈希表使用,将 nums[i]
放到其应该出现的位置上
python代码解法
class Solution:
def missingNumber(self, nums: List[int]) -> int:
def swap(arr, i, j):
temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
n = len(nums)
for i in range(0, n):
while nums[i] != i and nums[i] < n:
swap(nums, i, nums[i])
for i in range(0, n):
if i != nums[i]:
return i
return n
Leetcode41. 缺失的第一个正数:困难题 (详情点击链接见原题)
给你一个未排序的整数数组
nums
,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为O(n)
并且只使用常数级别额外空间的解决方案
题目要求我们只能使用常数级别的空间,而要找的数一定在 [1, N + 1]
(左闭右闭) 这个区间里,因此我们可以就把原始的数组当作哈希表来使用,我们要找的数就在 [1, N + 1]
里,最后 N + 1
这个元素我们不用找,因为在前面的 N
个元素都找不到的情况下我们才返回 N + 1
解题思路:
把数值为 i
的数映射到 下标为 i - 1
的位置,理解下面代码 nums[nums[i] - 1] != nums[i]
的作用,在 nums[i]
的范围是合法区间的前提下,我们就将 nums[i]
映射到它原本应在的位置上去
python代码解法
class Solution:
def swap(self, nums, a, b):
temp = nums[a]
nums[a] = nums[b]
nums[b] = temp
def firstMissingPositive(self, nums: List[int]) -> int:
n = len(nums)
for i in range(n):
while 1 <= nums[i] <= n and nums[i] != nums[nums[i] - 1]:
self.swap(nums, i, nums[i] - 1)
for i in range(n):
if i + 1 != nums[i]:
return i + 1
return n + 1
Leetcode1002. 查找共用字符:简单题 (详情点击链接见原题)
给你一个字符串数组
words
,请你找出所有在words
的每个字符串中都出现的共用字符( 包括重复字符),并以数组形式返回。你可以按 任意顺序 返回答案
解题思路:用二维数组代替哈希映射
python代码解法
class Solution:
def commonChars(self, words: List[str]) -> List[str]:
char_map = [[0 for _ in range(26)] for _ in range(len(words))]
for r in range(len(words)):
for c in range(len(words[r])):
char_map[r][ord(words[r][c]) - ord('a')] += 1
ans = []
for y in range(len(char_map[0])):
min_v = sys.maxsize
for x in range(len(char_map)):
min_v = min(min_v, char_map[x][y])
for _ in range(min_v):
ans.append(chr(ord('a') + y))
return ans
Leetcode560. 和为 K 的子数组:中等题 (详情点击链接见原题)
给你一个整数数组
nums
和一个整数k
,请你统计并返回 该数组中和为k
的子数组的个数
python代码解法1
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
sum_prefix = {0: 1} # 记录第一个和为k的数组
cur_num = 0
result = 0
for num in nums:
cur_num += num
if cur_num - k in sum_prefix:
result += sum_prefix[cur_num - k] # 中间出现连续0的情况
sum_prefix[cur_num] = sum_prefix.get(cur_num, 0) + 1
return result
Leetcode554:砖墙:中等题 (详情点击链接见原题)
你的面前有一堵矩形的、由
n
行砖块组成的砖墙。这些砖块高度相同(也就是一个单位高)但是宽度不同。每一行砖块的宽度之和相等。
我们可以使用【哈希表】记录每个间隙出现的次数,最终统计所有行中哪些间隙出现得最多,使用 总行数 减去 间隙出现的最多次数 即是答案
如何记录间隙呢?
直接使用行前缀和记录即可
python代码解法
class Solution:
def leastBricks(self, wall: List[List[int]]) -> int:
count = Counter()
for w in wall:
cur_sum = 0
for i in w:
cur_sum += i
count[cur_sum] += 1
count.pop(cur_sum) # 不能从两边穿过,需要 remove 掉最后一个
max_gap = 0
for k in count: # 求最少穿过的砖块数,即找出间隙号最多的间隙数
max_gap = max(max_gap, count[k])
return len(wall) - max_gap
Leetcode705.设计哈希集合:中等题 (详情点击链接见原题)
不使用任何内建的哈希表库设计一个哈希集合
(HashSet)
HashSet
是在时间和空间上做权衡的经典案例,如果不考虑空间,我们可以设计一个超大的数组,使每个key
都有单独的位置,则不存在冲突
设计哈希函数需要考虑的两个问题:
hash
方法把键 key
转成数组的索引,设计合适的 hash
函数,一般都是对分桶数取模 %
拉链法是说我们定义了一个比较小的数组,然后用hash
方法来求出 key
应该出现在数组中的位置,但是由于不同的 key
在求完 hash
之后可能会存在碰撞冲突,所以数组并不直接保存元素,而是每个位置都指向了一条链表(数组)用于存储元素
分桶数组:其实是拉链法的变形
大家别被这个陌生的专业名词吓到,其实很简单
python代码解法1
class MyHashSet:
def __init__(self):
self.buckets = 1000 # 1000个桶
self.itemsPerBucket = 1001 # 每个桶中预留1001个位置
self.table = [[] for _ in range(self.buckets)]
def add(self, key: int) -> None:
pos = key // self.buckets # 先确定桶中的具体位置(后面会用到)
key %= self.buckets # 确定key所在哪个桶中(因为这里会改变 key 值,所以 pos 放到前面计算)
if not self.table[key]:
self.table[key] = [0] * self.itemsPerBucket
self.table[key][pos] = 1
def remove(self, key: int) -> None:
pos = key // self.buckets # 先确定桶中的具体位置
key %= self.buckets # 确定key所在哪个桶中
if not self.table[key]:
return
if self.table[key][pos]:
self.table[key][pos] = 0
def contains(self, key: int) -> bool:
pos = key // self.buckets # 先确定桶中的具体位置
key %= self.buckets # 确定key所在哪个桶中
if not self.table[key]:
return False
return True if self.table[key][pos] else False
# Your MyHashSet object will be instantiated and called as such:
# obj = MyHashSet()
# obj.add(2)
# obj.remove(2)
# obj.remove(5)
# param_3 = obj.contains(2)
# print(param_3)
真正的拉链法:用链表实现的拉链法
python代码解法2
class ListNode:
def __init__(self, val=None, next=None):
self.val = val
self.next = next
class MyHashSet:
def __init__(self):
self.size = 1000
self.linklist = [ListNode() for _ in range(self.size)]
def add(self, key: int) -> None:
value = key
key %= self.size
p = self.linklist[key]
temp = p
while temp:
if temp.val == value:
return
temp = temp.next
pre = ListNode(value)
pre.next = p.next
p.next = pre
def remove(self, key: int) -> None:
value = key
key %= self.size
p = self.linklist[key]
pre = p
temp = p.next
while temp:
if temp.val == value:
pre.next = temp.next
pre = pre.next
temp = temp.next
def contains(self, key: int) -> bool:
value = key
key %= self.size
p = self.linklist[key]
temp = p
while temp:
if temp.val == value:
return True
temp = temp.next
return False
Leetcode146. LRU 缓存:中等题 (详情点击链接见原题)
请你设计并实现一个满足
LRU
(最近最少使用) 缓存 约束的数据结构
python代码解法
class ListNode:
def __init__(self, key=None, value=None):
self.key = key
self.value = value
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.hash_map = {}
self.head, self.tail = ListNode(), ListNode()
self.head.next = self.tail
self.tail.prev = self.head
def move_to_head(self, key):
"""
即双链表的一个查找和插入操作
:param key:
:return:
"""
node = self.hash_map[key] # 找到 key 对应的结点
# 将该节点与原链表断链
node.prev.next = node.next
node.next.prev = node.prev
# 将最近访问的节点放到头结点后
node.next = self.head.next
node.prev = self.head.next.prev
self.head.next.prev = node
self.head.next = node
def get(self, key: int) -> int:
if key in self.hash_map: # 如果关键字 key 已经存在
self.move_to_head(key) # 将该 key 对应的结点插入到头结点后更新为最近访问
return -1 if self.hash_map.get(key, -1) == -1 else self.hash_map[key].value # 返回关键字对应的值
def put(self, key: int, value: int) -> None:
if key in self.hash_map: # 如果要插入结点的 key 已存在
self.hash_map[key].value = value # 变更其数据值
self.move_to_head(key) # 将该 key 对应的结点插入到头结点后更新为最近访问
else:
if len(self.hash_map) == self.capacity: # 如果容量已满,去掉最久没有被访问的节点
self.hash_map.pop(self.tail.prev.key) # 去掉尾节点的前一个节点(最久未使用)
self.tail.prev.prev.next = self.tail
self.tail.prev = self.tail.prev.prev
new_node = ListNode(key, value) # 申请一个新结点
self.hash_map[key] = new_node # 将该结点与哈希表中的key建立映射关系
# 将新结点插入到头结点之后
new_node.next = self.head.next
new_node.prev = self.head.next.prev
self.head.next.prev = new_node
self.head.next = new_node
Leetcode3. 无重复字符的最长子串:中等题 (详情点击链接见原题)
给定一个字符串
s
,请你找出其中不含有重复字符的 最长子串 的长度
python代码解法
from collections import Counter
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
max_len = 0
slide_window = Counter() # 统计滑动窗口内不同元素的数目
left, right = 0, 0
while right < len(s):
slide_window[s[right]] += 1
while slide_window[s[right]] > 1:
slide_window[s[left]] -= 1
left += 1
right += 1
max_len = max(max_len, right - left)
return max_len
Leetcode128. 最长连续序列:中等题 (详情点击链接见原题)
给定一个未排序的整数数组
nums
,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度
hash_dict = {key :value}
这里的 key
很好理解,就是 nums
里的每一个元素,关键是 value
的定义,【哈希表存储每个端点值对应连续区间的长度】
python代码解法
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
hash_map = {}
max_len = 0
for num in nums:
if num not in hash_map:
left = hash_map.get(num - 1, 0)
right = hash_map.get(num + 1, 0)
cur_len = 1 + left + right
max_len = max(max_len, cur_len)
hash_map[num] = cur_len
hash_map[num - left] = cur_len
hash_map[num + right] = cur_len
return max_len
Leetcode792. 匹配子序列的单词数:中等题 (详情点击链接见原题)
给定字符串
s
和字符串数组words
, 返回words[i]
中是s
的子序列的单词个数
解题思路
将 words
中的所有单词根据首字母来分桶,即把所有单词按照首字母分到26个桶中,每个桶中存储的是以该字母开头的所有单词,然后我们从 s
的第一个字符开始遍历,假设当前字符为 a
,我们从 a
开头的桶中取出所有单词,如果此时单词长度为 1
,说明该单词已经匹配完毕,我们将答案 +1
,否则我们将单词的首字母去掉,然后放如下一个字母开头的桶中
python代码解法
from collections import defaultdict, deque
class Solution:
def numMatchingSubseq(self, s: str, words: List[str]) -> int:
hash_map = defaultdict(deque)
for w in words:
hash_map[w[0]].append(w)
ans = 0
for c in s:
for _ in range(len(hash_map[c])):
t = hash_map[c].popleft()
if len(t) == 1:
ans += 1
else:
hash_map[t[1]].append(t[1:])
return ans
Leetcode13. 罗马数字转整数:简单题 (详情点击链接见原题)
罗马数字包含以下七种字符:
I
,V
,X
,L
,C
,D
和M
。
python代码解法
class Solution:
def romanToInt(self, s: str) -> int:
char_map = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
pre = char_map[s[0]]
ans = 0
for i in range(1, len(s)):
cur = char_map[s[i]]
if cur <= pre:
ans += pre
else:
ans -= pre
pre = cur
ans += pre
return ans
LCR186. 文物朝代判断:简单题 (详情点击链接见原题)
展览馆展出来自
13
个朝代的文物,每排展柜展出5
个文物。某排文物的摆放情况记录于数组places
,其中places[i]
表示处于第i
位文物的所属朝代编号。其中,编号为0
的朝代表示未知朝代。请判断并返回这排文物的所属朝代编号是否连续(如遇未知朝代可算作连续情况)
解题思路
0
可以代替任何牌5
个朝代中最大的为 max_v
,最小的朝代为 min_v
,未知朝代除外,则需满足 max_v - min_v < 5
python代码解法
class Solution:
def checkDynasty(self, places: List[int]) -> bool:
repeat = {}
max_v, min_v = 0, 14
for place in places:
if place == 0:
continue
max_v = max(max_v, place)
min_v = min(min_v, place)
if place in repeat:
return False
repeat[place] = 1
return max_v - min_v < 5
Leetcode791. 自定义字符串排序:中等题 (详情点击链接见原题)
给定两个字符串
order
和s
。order
的所有字母都是 唯一 的,并且以前按照一些自定义的顺序排序
起始先用大小为 C=26
的数组 counts
对 s
的所有字符进行词频统计,随后根据 order
的优先级进行构造
若字符 x
在 order
中排于 y
前面,则先往答案追加 count[s]
个字符x
,再往答案追加 count[y]
个字符y
,并更新对应词频,最后将仅出现在s
中的字符追加到答案尾部
python代码解法
class Solution:
def customSortString(self, order: str, s: str) -> str:
counts = [0] * 26
ans = ''
for i in s: # 遍历s进行字符出现的频率统计
counts[ord(i) - ord('a')] += 1
# print(counts)
for o in order: # 遍历order字符串
num = ord(o) - ord('a')
if counts[num] >= 0:
ans += o * counts[num] # 按照order中出现的字符的顺序,逐个乘以字符出现的频率追加到ans末尾
counts[num] = 0 # 将对应字符出现频率置为0
for i in range(0, len(counts)): # 将s中的多余字符追加到ans的末尾
if counts[i] != 0:
ans += chr(ord('a') + i) * counts[i]
return ans
Leetcode1365. 有多少小于当前数字的数字:简单题 (详情点击链接见原题)
给你一个数组
nums
,对于其中每个元素nums[i]
,请你统计数组中比它小的所有数字的数目
python代码解法
class Solution:
def smallerNumbersThanCurrent(self, nums: List[int]) -> List[int]:
ans = []
temp = nums[:] # 准备一个副本
hash_map = {}
nums.sort()
# 构造哈希表的时候从后向前遍历,这样哈希表里存放的就是相同元素最左面的数值和下标了
for i in range(len(nums) - 1, -1, -1):
hash_map[nums[i]] = i
for i in temp:
ans.append(hash_map[i])
return ans
本文给大家介绍了哈希表在面试中的高频考题以及哈希表和其他算法结合的考察形式,可以看到哈希表再结合其他知识点的应用中还是稍稍有点难的,比较考察大家对于数据结构的基础知识(尤其是LRU和设计哈希集合这两道题),祝大家面试顺利,面试碰到原题,找到心仪的offer,冲冲冲~