哈希表(Hash Table,也叫散列表),是根据键(Key)而直接访问在内存存储位置的数据结构。哈希表通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做哈希函数,存放记录的数组称做哈希表。
目录
1. 两数之和
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
hashtable = dict()
for i, num in enumerate(nums):
if target - num in hashtable:
return [hashtable[target - num], i]
hashtable[nums[i]] = i
return []
运行过程中hashtable如下所示,键key为nums中的元素值,值value为nums中的下标。
hashtable[nums[i]] = i 中, hashtable[key] = value
3. 无重复字符的最长子串
因此随着子串的起始位置递增,子串的结束位置也是递增的。可以使用滑动窗口解决问题。
如果用暴力解法,时间复杂度为O(n^2),相当于两层遍历。使用哈希表(代码中设置的集合存储不重复元素)只需要遍历一次。
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
hashtable = set()
n = len(s)
# 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
rightFlag, maxLength = -1, 0
for leftFlag in range(n):
if leftFlag != 0:
# 左指针向右移动一格,移除一个字符
# 从集合set中删除数据用remove
hashtable.remove(s[leftFlag - 1])
while rightFlag + 1 < n and s[rightFlag + 1] not in hashtable:
# 不断地移动右指针
# 向集合set中添加数据用add
hashtable.add(s[rightFlag + 1])
rightFlag += 1
# 第 leftFlag 到 rightFlag 个字符是一个极长的无重复字符子串
maxLength = max(maxLength, rightFlag - leftFlag + 1)
return maxLength
12. 整数转罗马数字
class Solution(object):
def intToRoman(self, num):
"""
:type num: int
:rtype: str
"""
hashtable = {1000:'M', 900:'CM', 500:'D', 400:'CD',
100:'C', 90:'XC', 50:'L', 40:'XL', 10:'X',
9:'IX', 5:'V', 4:'IV', 1:'I'}
li = ''
while num > 0:
for key in hashtable:
if num - key >= 0:
li += hashtable[key]
num -= key
return li
理论上是没错的不知道为什么在线结果和离线结果不一样,另一种能通过的解法:
class Solution:
THOUSANDS = ["", "M", "MM", "MMM"]
HUNDREDS = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"]
TENS = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]
ONES = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]
def intToRoman(self, num):
return Solution.THOUSANDS[num // 1000] + \
Solution.HUNDREDS[num % 1000 // 100] + \
Solution.TENS[num % 100 // 10] + \
Solution.ONES[num % 10]
17. 电话号码的字母组合
class Solution(object):
def letterCombinations(self, digits):
"""
:type digits: str
:rtype: List[str]
"""
if not digits:
return []
hashtable = {
"2": "abc",
"3": "def",
"4": "ghi",
"5": "jkl",
"6": "mno",
"7": "pqrs",
"8": "tuv",
"9": "wxyz",
}
def backtrack(index):
if index == len(digits):
res.append("".join(path))
else:
digit = digits[index]
for letter in hashtable[digit]:
path.append(letter)
backtrack(index + 1)
path.pop()
path = []
res = []
backtrack(0)
return res
30. 串联所有单词的子串
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
if not words:
return []
singleword_len, s_len = len(words[0]), len(s)
t_len = singleword_len * len(words) # 子串的长度
word_dict = {} # words的哈希表
for word in words:
word_dict[word] = word_dict.get(word, 0) + 1
ans = []
for offset in range(singleword_len):
lo, lo_max = offset, s_len - t_len
while lo <= lo_max:
tmp_dict = word_dict.copy()
match = True
for hi in range(lo + t_len, lo, -singleword_len): # 从尾到头搜索单词
word = s[hi - singleword_len: hi]
if word not in tmp_dict or tmp_dict.get(word, 0) == 0:
match = False
break # 当前单词不符合要求 直接停止这个子串的搜索
tmp_dict[word] -= 1
if match:
ans.append(lo)
lo = hi
return ans
41. 缺失的第一个正数
哈希表的优势在于:哈希表是一个可以支持快速查找的数据结构:给定一个元素,我们可以在 O(1) 的时间查找该元素是否在哈希表中。
1. 假设列表中的数为[1,n],只检验[1,n]范围内是否有缺失的正整数,因此将负数标记为n+1不干扰最后结果;
2. 遍历列表,当元素在[1,n]范围内时,将nums对应位置更改为负数,起到标记作用(当nums[i]为负数时,说明存在i+1);
3. 返回列表中第一个为正整数的下标+1,如果全都为负数(全都被标记)则返回n+1。
class Solution(object):
def firstMissingPositive(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
n = len(nums)
for i in range(n):
if nums[i] <= 0:
nums[i] = n + 1
for i in range(n):
num = abs(nums[i])
if num <= n:
nums[num - 1] = -abs(nums[num - 1])
for i in range(n):
if nums[i] > 0:
return i + 1
return n + 1
49. 字母异位词分组
class Solution(object):
def groupAnagrams(self, strs):
"""
:type strs: List[str]
:rtype: List[List[str]]
"""
hashtable = dict()
for i in range(len(strs)):
word = ''.join(sorted(strs[i]))
if word not in hashtable:
hashtable[word] = [strs[i]]
else:
hashtable[word].append(strs[i])
return list(hashtable.values())
或者用collections库函数defaultdict排序:
import collections
class Solution:
def groupAnagrams(self, strs):
mp = collections.defaultdict(list)
for st in strs:
key = "".join(sorted(st))
mp[key].append(st)
return list(mp.values())
73. 矩阵置零
class Solution(object):
def setZeroes(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: None Do not return anything, modify matrix in-place instead.
"""
n_row , n_col = len(matrix), len(matrix[0])
row = []
col = []
for i in range(n_row):
for j in range(n_col):
if matrix[i][j] == 0:
row.append(i)
col.append(j)
for val in row:
for j in range(n_col):
matrix[val][j] = 0
for val in col:
for i in range(n_row):
matrix[i][val] = 0
return matrix
用矩阵中的第一行和第一列标记该行/列是否有0
76. 最小覆盖子串
import collections
class Solution(object):
def minWindow(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
# 创建一个字典表示当前滑动窗口中需要各元素的数量
need = collections.defaultdict(int)
for val in t:
need[val] += 1
needCnt = len(t)
left = 0
res = (0, float('inf'))
for right, val in enumerate(s):
if need[val] > 0:
needCnt -= 1
need[val] -= 1
if needCnt == 0: # 步骤一:滑动窗口包含了所有T元素
while True: # 步骤二:增加i,排除多余元素
val = s[left]
if need[val] == 0: # 多余元素的val小于0
break
need[val] += 1
left += 1
if right - left < res[1] - res[0]: # 记录结果
res = (left, right)
need[s[left]] += 1 # 步骤三:i增加一个位置,寻找新的满足条件滑动窗口
needCnt += 1
left += 1
return '' if res[1] > len(s) else s[res[0]:res[1] + 1] # 如果res始终没被更新过,代表无满足条件的结果
利用一个滑动窗口记录子串情况,right负责向右延申,left负责向右缩进,将子串中元素情况记录在哈希表need中。代码中need = collections.defaultdict(int)与need = {} 的区别在于,可以直接使用need[val] += 1
Python中通过Key访问字典,当Key不存在时,会引发‘KeyError’异常。为了避免这种情况的发生,可以使用collections类中的defaultdict()方法来为字典提供默认值。
105. 从前序与中序遍历序列构造二叉树
# Definition for a binary tree node.
class TreeNode(object):
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution(object):
def buildTree(self, preorder, inorder):
"""
:type preorder: List[int]
:type inorder: List[int]
:rtype: TreeNode
"""
def myBuildTree(preorder_left, preorder_right, inorder_left, inorder_right):
if preorder_left > preorder_right:
return None
# 前序遍历中的第一个节点就是根节点
preorder_root = preorder_left
# 在中序遍历中定位根节点
inorder_root = index[preorder[preorder_root]]
# 先把根节点建立出来
root = TreeNode(preorder[preorder_root])
# 得到左子树中的节点数目
size_left_subtree = inorder_root - inorder_left
# 递归地构造左子树,并连接到根节点
# 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root.left = myBuildTree(preorder_left + 1, preorder_left + size_left_subtree, inorder_left,
inorder_root - 1)
# 递归地构造右子树,并连接到根节点
# 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root.right = myBuildTree(preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1,
inorder_right)
return root
n = len(preorder)
# 构造哈希映射,帮助我们快速定位根节点
index = {element: i for i, element in enumerate(inorder)}
return myBuildTree(0, n - 1, 0, n - 1)
没太看明白,做到二叉树的时候再看一遍吧。
126. 单词接龙
第一种方法:建立邻接图,找到所有相邻的点
import collections
class Solution(object):
def findLadders(self, beginWord, endWord, wordList):
"""
:type beginWord: str
:type endWord: str
:type wordList: List[str]
:rtype: List[List[str]]
"""
m = len(wordList)
# endWord不在wordList中
if endWord not in set(wordList):
return []
# 判断word1和word2中是否只有一个字母不同
def is_neighbor(word1, word2):
cnt_diff = 0
for i in range(len(word1)):
if word1[i] != word2[i]:
cnt_diff += 1
if cnt_diff > 1:
return False
return True
# 建立邻接图
neighbors = collections.defaultdict(list)
# neighbors[i]存储与word[i]有一个字母不同的word的下标
for i in range(m):
for j in range(i+1, m):
if is_neighbor(wordList[i], wordList[j]):
neighbors[i].append(j)
neighbors[j].append(i)
# 找出beginWord的所有邻居
begins = [i for i in range(m) if is_neighbor(beginWord, wordList[i])]
# beginWord没有邻居
if len(begins) == 0:
return False
# 广度优先找最短路径
all_paths = [] # 存储可到达路径
que = [[pos] for pos in begins] # 存储未到达路径
visited = set() # 存储所有走过的点
while que:
visited_add = set() # 存储当前step走过的点
for _ in range(len(que)):
cur_path = que.pop(0)
cur_pos = cur_path[-1]
if wordList[cur_pos] == endWord:
all_paths.append(cur_path)
else:
for next_pos in neighbors[cur_pos]:
# 如果在之前的step中遍历过next_pos则没必要再走
if next_pos in visited:
continue
que.append(cur_path + [next_pos])
visited_add.add(next_pos)
visited = visited | visited_add
if len(all_paths) > 0:
break
all_paths_words = [[beginWord] + [wordList[i] for i in l] for l in all_paths]
return all_paths_words
第二种方法:优化的BFS(广度优先搜索)
1. 把每个单词看作可以hash成c个值,c代表单词长度,比如单词'hit',可以hash成'*it','h*t','hi*'这3个值,如果2个单词的hash值有重合,说明这2个单词是可以通过改1个字母变成另外一个;
2. 双向BFS:分别从beginWord和endWord开始遍历,当哪边的节点少就遍历哪边,当两边的节点存在交集时,遍历结束。如果一直没交集,任何一头走到末尾就可以结束,代表全部节点都遍历完成。
import collections
class Solution:
def findLadders(self, beginWord, endWord, wordList):
if endWord not in wordList:
return []
hashtable = collections.defaultdict(list)
# hashtable存储格式符合要求的单词(h*t:[hot])
for word in wordList:
for i in range(len(word)):
hashtable[word[:i] + "*" + word[i + 1:]].append(word)
def findNeighbor(word):
for i in range(len(word)):
# 遍历符合格式条件的单词(符合格式条件说明newWord是word的neighbor)
for newWord in hashtable[word[:i] + '*' + word[i + 1:]]:
if newWord not in visited:
yield newWord
def findPath(end):
res = []
for curr in end:
# key存放右面的word,value存放左面的word,已知右面的word找它左面所有word
for parent in path[curr[0]]:
res.append([parent] + curr)
return res
visited = set()
path = collections.defaultdict(set)
begin = {beginWord} # 记录最后一层所有节点(上一次循环的visited_add)
end = {endWord}
forward = True # forward为True时从begin开始遍历,False从end开始遍历
while begin and end:
if len(begin) > len(end):
# 从节点少的一端开始遍历,begin一端节点多时头尾互换
begin, end = end, begin
forward = not forward
visited_add = set() # 该层的访问过的集合
for word in begin:
visited.add(word)
# 先把begin一端所有节点添加到visited防止重复遍历
for word in begin:
for next_word in findNeighbor(word):
visited_add.add(next_word)
if forward:
# 如果从begin一端开始遍历的,key存放右面的word,value存放左面的word
path[next_word].add(word)
else:
# 如果从end一端开始遍历的,key存放右面的word,value存放左面的word
path[word].add(next_word)
begin = visited_add
if begin & end: # 当begin和end存在交集
res = [[endWord]]
while res[0][0] != beginWord:
res = findPath(res)
return res
return []
127. 单词接龙
import collections
class Solution:
def ladderLength(self, beginWord, endWord, wordList):
wordSet = set(wordList) # 将wordList转为set格式会快很多
if endWord not in wordSet:
return 0
l_queue = [beginWord]
r_queue = [endWord]
l_visited = {beginWord} # 初始化为集合set
r_visited = {endWord}
depth = 1
neighbors = collections.defaultdict(list)
for word in wordSet:
for i in range(len(word)):
neighbors[word[:i] + '*' + word[i+1:]].append(word)
while l_queue and r_queue:
if len(l_queue) > len(r_queue):
# 每次都走节点少的一端
l_queue, r_queue = r_queue, l_queue
l_visited, r_visited = r_visited, l_visited
for _ in range(len(l_queue)): # 每层开始遍历
cur = l_queue.pop(0)
if cur in r_visited: # 当前节点在另一端遍历过
return depth
for i in range(len(beginWord)):
for nextWord in neighbors[cur[:i] + '*' + cur[i+1:]]:
if nextWord not in l_visited:
l_queue.append(nextWord)
l_visited.add(nextWord)
depth += 1
return 0
128. 最长连续序列
class Solution(object):
def longestConsecutive(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
hashtable = {}
maxlength = 0
for num in nums:
if num not in hashtable:
left = hashtable.get(num-1, 0)
right = hashtable.get(num+1, 0)
currentlength = right + left + 1
maxlength = max(maxlength, currentlength)
hashtable[num] = currentlength
hashtable[num + right] = currentlength
hashtable[num - left] = currentlength
return maxlength
right记录在num右边还有几个连续的数,left记录num左边还有几个连续的数,每个num循环结束更新两边端点的currentlength值,记录当前最长长度。
dict.get(key, n) 返回字典中key对应的value,如果不存在则返回n。
或者第二种思路:
import collections
class Solution(object):
def longestConsecutive(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
# 返回key为元素,value为元素个数的字典
cnts = collections.Counter(nums)
maxlength = 1
for num in cnts:
# 当num-1不在字典中,说明num是一串连续数的开始
if num - 1 in cnts:
continue
else:
currentlength = 0
next = num
while next in cnts:
currentlength += 1
next += 1
maxlength = max(maxlength, currentlength)
return maxlength
当num-1不在字典中时(即num为一串连续数的开始)检查该串连续数的长度,其中cnts是记录nums中元素种类和个数的字典。
138. 复制带随机指针的列表
class Node:
def __init__(self, x, next=None, random=None):
self.val = int(x)
self.next = next
self.random = random
class Solution(object):
def copyRandomList(self, head):
"""
:type head: Node
:rtype: Node
"""
if not head:
return None
# 创建一个哈希表,key是原节点,value是新节点
hashtable = dict()
p = head
while p:
new_node = Node(p.val, None, None)
hashtable[p] = new_node
p = p.next
p = head
while p:
if p.next:
hashtable[p].next = hashtable[p.next]
if p.random:
hashtable[p].random = hashtable[p.random]
p = p.next
return hashtable[head]
139. 单词拆分
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: bool
"""
s_len = len(s)
dp = [False for _ in range(s_len + 1)]
dp[0] = True
for i in range(s_len):
# i为开始索引,j为结束索引
for j in range(i + 1, s_len + 1):
if dp[i] == True and s[i:j] in wordDict:
dp[j] = True
return dp[s_len]
另外一种方法采用回溯思想:
在如下样例中存在大量重复计算情况下可能会超时:
使用一个数组存储计算结果,数组索引为指针位置(以该指针作为起始位置的计算结果),值为计算的结果,下次遇到相同的子问题直接返回数组中的缓存值:
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: bool
"""
memory = {}
def dfs(s, cur):
# 如果已经算过了,直接返回
if cur in memory:
return memory[cur]
# 拆分完成
if cur == len(s):
memory[cur] = True
return True
for word in wordDict:
# 如果当前的单词比剩下的字符串还要长,则不必查了以防越界
if len(word) > len(s) - cur:
continue
# 如果下一个单词能成功拆分且按这样拆下去能成功则返回True
elif s[cur:cur + len(word)] == word and dfs(s, cur + len(word)):
memory[cur] = True
return True
# 没有一种情况拆分成功
memory[cur] = False
return False
return dfs(s, 0)
140. 单词拆分Ⅱ
动态规划:
from functools import lru_cache
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: List[str]
"""
@lru_cache(None)
def backtrack(index) :
if index == len(s):
return [[]]
ans = list()
for i in range(index + 1, len(s) + 1):
word = s[index:i]
if word in wordSet:
nextWordBreaks = backtrack(i)
for nextWordBreak in nextWordBreaks:
ans.append(nextWordBreak.copy() + [word])
return ans
wordSet = set(wordDict)
breakList = backtrack(0)
return [" ".join(words[::-1]) for words in breakList]
@Iru_cache(None) 在第二次调用函数时不进行计算直接返回缓存结果。
第二种方法,使用memo[i:]代表s[i:]这个字符串能否被分割,如果不能停止后续遍历。(其实和第一种是一样的但是第一种在python3里运行)
class Solution:
# 带备忘录的记忆化搜索
def wordBreak(self, s, wordDict):
res = []
memo = [1] * (len(s) + 1)
wordDict = set(wordDict)
def dfs(wordDict, temp, pos):
num = len(res) # 回溯前先记下答案中有多少个元素
if pos == len(s):
res.append(" ".join(temp))
return
for i in range(pos, len(s) + 1):
if memo[i] and s[pos:i] in wordDict: # 添加备忘录的判断条件
temp.append(s[pos:i])
dfs(wordDict, temp, i)
temp.pop()
# 答案中的元素没有增加,说明s[pos:]不能分割,修改备忘录
memo[pos] = 1 if len(res) > num else 0
dfs(wordDict, [], 0)
return res
141. 环形链表
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
if not head:
return False
hash = set()
hash.add(head)
now = head
while now.next:
now = now.next
if now in hash:
return True
else:
hash.add(now)
return False
# Definition for singly-linked list.
class ListNode(object):
def __init__(self, x):
self.val = x
self.next = None
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
if not head or not head.next:
return False
slow = head
fast = head.next
while slow != fast:
if not fast or not fast.next:
return False
slow = slow.next
fast = fast.next.next
return True
142. 环形链表Ⅱ
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
fast, slow = head, head
while True:
if not (fast and fast.next): return
fast, slow = fast.next.next, slow.next
if fast == slow: break
fast = head
while fast != slow:
fast, slow = fast.next, slow.next
return fast
构建两次相遇:
149. 直线上最多的点数
class Solution(object):
def maxPoints(self, points):
"""
:type points: List[List[int]]
:rtype: int
"""
n = len(points)
if n <= 2:
return n
res = 2
for i in range(n):
x1, y1 = points[i][0], points[i][1]
has = {}
for j in range(i + 1, n):
x2, y2 = points[j][0], points[j][1]
if x1 == x2:
a, b, c = 1, 0, -x1
elif y1 == y2:
a, b, c = 0, 1, -y1
else:
a = 1.0
b = 1.0 * (x1 - x2) / (y2 - y1)
c = 1.0 * (x1 * y2 - x2 * y1) / (y1 - y2)
if (a,b,c) in has.keys():
has[(a,b,c)]+=1
res = max(res,has[(a,b,c)])
else:
has[(a,b,c)] = 2
return res
字典中的key可以使用任意不可变数据(整数、实数、复数、字符串、元组等可散列数据),不能使用可变类型(列表、集合、字典)。
160. 相交链表
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
nowA, nowB = headA, headB
while nowA != nowB:
nowA = nowA.next if nowA else headB
nowB = nowB.next if nowB else headA
return nowA
两个指针分别从两个链表的head开始遍历,当其中一个遍历完之后从另一个链表的head继续遍历。
166. 分数到小数
import collections
class Solution(object):
def fractionToDecimal(self, numerator, denominator):
"""
:type numerator: int
:type denominator: int
:rtype: str
"""
ans = []
flag = True # 标记结果的正负
if numerator == 0:
return '0'
if (numerator < 0 and denominator > 0) or (numerator > 0 and denominator < 0):
# if (numerator < 0) != (denominator < 0):
flag = False
numerator = abs(numerator)
denominator = abs(denominator)
if not flag:
ans.append('-')
# 整数部分
tmp = numerator // denominator
ans.append('%d' % tmp)
# ans.append(str(tmp))
remainder = numerator % denominator
if remainder == 0:
return ''.join(ans)
ans.append('.')
# 小数部分
hashtable = collections.defaultdict(int) # 记录余数第一次出现的下标
while remainder and remainder not in hashtable:
hashtable[remainder] = len(ans)
remainder *= 10
ans.append('%d' % (remainder // denominator))
remainder %= denominator
if remainder:
start = hashtable[remainder]
ans.insert(start, '(')
ans.append(')')
return ''.join(ans)
187. 重复的DNA序列
import collections
class Solution(object):
def findRepeatedDnaSequences(self, s):
"""
:type s: str
:rtype: List[str]
"""
hashtable = collections.defaultdict(int)
if len(s) < 10:
return []
for left in range(len(s)-9):
dna = s[left:left+10]
hashtable[str(dna)] += 1
ans = []
for key in hashtable:
if hashtable[key] > 1:
ans.append(key)
return ans
202. 快乐数
class Solution(object):
def isHappy(self, n):
"""
:type n: int
:rtype: bool
"""
hashtable = set()
tmp = n
while tmp not in hashtable and tmp != 1:
hashtable.add(tmp)
num = []
count = len(str(tmp)) # 数字位数
for i in range(count-1, -1, -1):
num.append(tmp // (10 ** i))
tmp = tmp % (10 ** i)
tmp = 0
for i in range(len(num)):
tmp += num[i] ** 2
if tmp != 1:
return False
else:
return True
或者使用快慢指针,指针相遇时返回False
205. 同构字符串
import collections
class Solution(object):
def isIsomorphic(self, s, t):
"""
:type s: str
:type t: str
:rtype: bool
"""
hashtable_st = collections.defaultdict(str) # 记录s到t的映射关系
hashtable_ts = set() # 检验是否有相同两个字符映射到同一字符
for i, letter in enumerate(s):
if letter not in hashtable_st:
if t[i] not in hashtable_ts:
hashtable_st[letter] = t[i]
hashtable_ts.add(t[i])
else:
return False
else:
if hashtable_st[letter] != t[i]:
return False
return True
229. 求众数
import collections
class Solution(object):
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
hashtable = collections.defaultdict(int)
ans = []
for num in nums:
hashtable[num] += 1
for num in hashtable:
if hashtable[num] > len(nums) // 3:
ans.append(num)
return ans
242. 有效的字母异位词
class Solution(object):
def isAnagram(self, s, t):
"""
:type s: str
:type t: str
:rtype: bool
"""
hashtable_s = collections.defaultdict(int)
hashtable_t = collections.defaultdict(int)
for letter in s:
hashtable_s[letter] += 1
for letter in t:
hashtable_t[letter] += 1
return hashtable_s == hashtable_t
264. 丑数
import heapq
class Solution(object):
def nthUglyNumber(self, n):
"""
:type n: int
:rtype: int
"""
factors = [2, 3, 5]
visited = {1} # 遍历过的丑数集合
heap = [1] # 堆顶
for i in range(n - 1):
curr = heapq.heappop(heap) # 从堆中弹出最小值
for factor in factors:
if (curr * factor) not in visited:
nxt = curr * factor
visited.add(nxt)
heapq.heappush(heap, nxt) # 往堆中插入新的值
return heapq.heappop(heap)
heapq常见应用:
heap = [] #创建了一个空堆
heappush(heap,item) # 往堆中插入一条新的值
item = heappop(heap) # 从堆中弹出最小值
item = heap[0] # 查看堆中最小值,不弹出
heapify(x) # 以线性时间将一个列表转化为堆
item = heapreplace(heap,item) # 弹出并返回最小值,然后将item的值插入到堆中,堆的整体结构不会发生改变。
堆的方法比较慢,也可以采用动态规划的方法:
设置三个指针p2,p3,p5,dp[i]=min(dp[p2]×2,dp[p3]×3,dp[p5]×5),并将对应指针加一。
class Solution(object):
def nthUglyNumber(self, n):
"""
:type n: int
:rtype: int
"""
ans = [0, 1]
p2 = p3 = p5 = 1
for i in range(2, n + 1):
ans.append(min(ans[p2] * 2, ans[p3] * 3, ans[p5] * 5))
if ans[i] == ans[p2] * 2:
p2 += 1
if ans[i] == ans[p3] * 3:
p3 += 1
if ans[i] == ans[p5] * 5:
p5 += 1
return ans
313. 超级丑数
class Solution(object):
def nthSuperUglyNumber(self, n, primes):
"""
:type n: int
:type primes: List[int]
:rtype: int
"""
len_primes = len(primes)
pointers = [1] * (len_primes)
num = [0] * (len_primes)
ans = [0, 1]
for i in range(2, n+1):
for j in range(len_primes):
num[j] = ans[pointers[j]] * primes[j]
next = min(num)
for j in range(len_primes):
if num[j] == next:
pointers[j] += 1
ans.append(next)
return ans[n]
这种方法超出时间限制了,可以通过以下方法优化(减少一个for循环):
class Solution(object):
def nthSuperUglyNumber(self, n, primes):
"""
:type n: int
:type primes: List[int]
:rtype: int
"""
ans = [0] * (n + 1)
m = len(primes)
pointers = [0] * m
num = [1] * m
for i in range(1, n + 1):
next = min(num)
ans[i] = next
for j in range(m):
if num[j] == next:
pointers[j] += 1
num[j] = ans[pointers[j]] * primes[j]
return ans[n]
或者采用堆排序:
import heapq
class Solution(object):
def nthSuperUglyNumber(self, n, primes):
"""
:type n: int
:type primes: List[int]
:rtype: int
"""
# ans[i] 代表第i+1个丑数
ans = [1] * n
# 丑数, 刚刚乘过的丑数的坐标, 质因数
pq = [(p, 0, i) for i, p in enumerate(primes)]
for i in range(1, n):
# 目前最新的最小的丑数
ans[i] = pq[0][0]
# 所有等于这个值的要全部出队列,并根据该乘的丑数重新加入队列
while pq and pq[0][0] == ans[i]:
_, idx, p = heapq.heappop(pq)
heapq.heappush(pq, (ans[idx + 1] * primes[p], idx + 1, p))
return ans[-1]