leetcode-哈希表
哈希表(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]
-
"""
-
-
-
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]
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgaghkf
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13