字符串问题
LeetCode242题 有效的字母异位词
class Solution(object):
def isAnagram(self, s, t):
d = dict()
for i in s:
if i in d:
d[i] += 1
else:
d[i] = 1
for j in t:
if j not in d:
return False
else:
d[j] -= 1
for k, v in d.items():
if v != 0:
return False
return True
LeetCode49题 字母异位词分组
python的字典可以用元组作为key,可以先对每个字符串的组成字母计数,然后用计数的元组作为key,将相同的字母异位词放在一个组中
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
d = dict()
for s in strs:
tmp = [0] * 26
for i in s:
tmp[ord(i) - ord('a')] += 1
d[tuple(tmp)] = d.get(tuple(tmp), []) + [s]
return list(d.values())
LeetCode205题 同构字符串
需要两个哈希表记录正反两种映射关系
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
k_v = dict()
v_k = dict()
for i in range(len(s)):
if (s[i] in k_v and k_v[s[i]] != t[i]) or (t[i] in v_k and v_k[t[i]] != s[i]):
return False
k_v[s[i]] = t[i]
v_k[t[i]] = s[i]
return True
LeetCode1002题 查找共用字符
先统计第一个字符串所有字符出现的次数,放在d中
之后把其他字符串里字符的出现次数也统计出来一次放在tmp_d中
然后用tmp_d更新d,相同的key对应的value取两个哈希表的最小值
最后d中剩的字符数量,就是答案
class Solution:
def commonChars(self, words: List[str]) -> List[str]:
res = []
d = dict()
for s in words[0]:
d[s] = d.get(s, 0) + 1
for w in words[1:]:
tmp_d = dict()
for s in w:
if s in d:
tmp_d[s] = tmp_d.get(s, 0) + 1
for k, v in tmp_d.items():
tmp_d[k] = min(tmp_d[k], d[k])
d = tmp_d
for k, v in d.items():
res.extend([k] * v)
return res
数组问题
LeetCode202题 快乐数
将每一次平方和结果存在哈希表中,如果平方和重复出现,则会无限循环下去,不会是快乐数
class Solution:
def isHappy(self, n: int) -> bool:
visited = set()
while n != 1:
total = 0
for i in str(n):
total += int(i)**2
if total in visited:
return False
visited.add(total)
n = total
return True
LeetCode454题 四数相加II
这道题目是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于四数之和简单了不少,用哈希表就可以解决
class Solution(object):
def fourSumCount(self, nums1, nums2, nums3, nums4):
res = 0
n = len(nums1)
d1 = dict()
d2 = dict()
for i in range(n):
for j in range(n):
s1 = nums1[i] + nums2[j]
s2 = nums3[i] + nums4[j]
d1[s1] = d1.get(s1, 0) + 1
d2[s2] = d2.get(s2, 0) + 1
for k, v in d1.items():
if -k in d2:
res += v * d2[-k]
return res
N数之和问题
LeetCode1题 两数之和
class Solution(object):
def twoSum(self, nums, target):
d = dict()
for i, j in enumerate(nums):
if target - j in d:
return [d[target - j], i]
d[j] = i
LeetCode15题 三数之和
在数组中找到abc使得a + b +c =0,可以排序数组后,固定a = nums[i],然后使用双指针确定bc:b = nums[left],c = nums[right],将复杂度降为O(n^2)
双指针移动方法:如果nums[i] + nums[left] + nums[right] > 0,说明三数之和大了,right就向左移动;如果nums[i] + nums[left] + nums[right] < 0,说明三数之和小了,left就向右移动;直到left与right相遇为止
去重:主要考虑abc三个数的去重。对于a的去重,如果nums[i]与nums[i-1]相同,则跳过对应的i;对于bc的去重,需要在收集到结果后,移动left、right指针,将重复的bc去掉
class Solution(object):
def threeSum(self, nums):
res = []
nums.sort()
for i in range(len(nums)):
# 剪枝
if nums[i] > 0:
return res
# 去重
if i > 0 and nums[i] == nums[i - 1]:
continue
left = i + 1
right = len(nums) - 1
while left < right:
if nums[i] + nums[left] + nums[right] > 0:
right -= 1
elif nums[i] + nums[left] + nums[right] < 0:
left += 1
else:
res.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1
# 去重
while left < right and nums[left] == nums[left - 1]:
left += 1
while left < right and nums[right] == nums[right + 1]:
right -= 1
return res
LeetCode18题 四数之和
和三数之和思路相同,四数之和的双指针解法是两层for循环nums[i] + nums[j]为确定值,然后循环内有left和right下标作为双指针,找出nums[i] + nums[j] + nums[left] + nums[right] == target的情况,四数之和的时间复杂度是O(n^3)
class Solution(object):
def fourSum(self, nums, target):
n = len(nums)
res = []
if n < 4:
return res
nums.sort()
for i in range(n):
# 剪枝
if nums[i] > 0 and nums[i] > target:
return res
# 去重
if i - 1 >= 0 and nums[i] == nums[i - 1]:
continue
for j in range(i + 1, n):
# 剪枝
if nums[i] + nums[j] > 0 and nums[i] + nums[j] > target:
break
# 去重
if j - 1 >= i + 1 and nums[j] == nums[j - 1]:
continue
left = j + 1
right = n - 1
while left < right:
sum_ = nums[i] + nums[j] + nums[left] + nums[right]
if sum_ > target:
right -= 1
elif sum_ < target:
left += 1
else:
res.append([nums[i], nums[j], nums[left], nums[right]])
right -= 1
left += 1
# 去重
while left < right and nums[left] == nums[left - 1]:
left += 1
while left < right and nums[right] == nums[right + 1]:
right -= 1
return res
数据结构设计
LeetCode380题 O(1)时间插入、删除和获取随机元素
如果想高效地,等概率地随机获取元素,就要使用数组作为底层容器
可以使用数组记录元素,使用哈希表记录val和数组索引的映射关系。删除元素时,为了保持数组元素的紧凑性,先将要删除的元素和数组末尾元素交换位置,然后将数组最后一个元素删除即可
class RandomizedSet:
def __init__(self):
self.nums = []
self.val_to_index = {}
def insert(self, val: int) -> bool:
if val not in self.val_to_index:
self.nums.append(val)
self.val_to_index[val] = len(self.nums) - 1
return True
else:
return False
def remove(self, val: int) -> bool:
if val in self.val_to_index:
i1, i2 = self.val_to_index[val], len(self.nums) - 1
if i1 != i2:
self.val_to_index[self.nums[i2]] = i1
self.nums[i1], self.nums[i2] = self.nums[i2], self.nums[i1]
self.nums.pop()
del self.val_to_index[val]
return True
else:
return False
def getRandom(self) -> int:
import random
i = random.randint(0, len(self.nums) - 1)
return self.nums[i]
LeetCode710题 黑名单中的随机数
思路类似380题,对于区间[0, n - len(blacklist) - 1]中的黑名单数,需要找到他们和区间[n - len(blacklist), n - 1]中非黑名单数的一个映射。然后可以在区间[0, n - len(blacklist) - 1]选一个随机数,如果这个数在黑名单中,我们就取它映射的那个数,如果不在黑名单,直接把这个数作为结果
import random
class Solution:
def __init__(self, n: int, blacklist: List[int]):
self.size = n - len(blacklist)
self.hash_map = dict()
last = n - 1
for i in blacklist:
self.hash_map[i] = i
for i in blacklist:
# 剪枝,若i >= self.size,此时没必要找到黑名单数字的映射关系
if i >= self.size:
continue
while last in self.hash_map:
last -= 1
self.hash_map[i] = last
last -= 1
def pick(self) -> int:
r = random.randint(0, self.size - 1)
if r in self.hash_map:
return self.hash_map[r]
else:
return r