• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

leetcode-哈希表

武飞扬头像
SunYutong_1234
帮助1

哈希表(Hash Table,也叫散列表),是根据键(Key)而直接访问在内存存储位置的数据结构。哈希表通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做哈希函数,存放记录的数组称做哈希表。

目录

1. 两数之和

 3. 无重复字符的最长子串

12. 整数转罗马数字

17. 电话号码的字母组合

 30. 串联所有单词的子串

41. 缺失的第一个正数

49. 字母异位词分组

73. 矩阵置零

76. 最小覆盖子串

105. 从前序与中序遍历序列构造二叉树

126. 单词接龙

127. 单词接龙

128. 最长连续序列

138. 复制带随机指针的列表

139. 单词拆分

140. 单词拆分Ⅱ

141. 环形链表

142. 环形链表Ⅱ

 149. 直线上最多的点数

160. 相交链表

 166. 分数到小数

187. 重复的DNA序列

 202. 快乐数

205. 同构字符串

229. 求众数

242. 有效的字母异位词

264. 丑数

313. 超级丑数


1. 两数之和

学新通

学新通

  1.  
    class Solution(object):
  2.  
    def twoSum(self, nums, target):
  3.  
    """
  4.  
    :type nums: List[int]
  5.  
    :type target: int
  6.  
    :rtype: List[int]
  7.  
    """
  8.  
    hashtable = dict()
  9.  
    for i, num in enumerate(nums):
  10.  
    if target - num in hashtable:
  11.  
    return [hashtable[target - num], i]
  12.  
    hashtable[nums[i]] = i
  13.  
    return []

 运行过程中hashtable如下所示,键key为nums中的元素值,值value为nums中的下标。

 hashtable[nums[i]] = i 中, hashtable[key] = value

学新通

 3. 无重复字符的最长子串

学新通

学新通

 因此随着子串的起始位置递增,子串的结束位置也是递增的。可以使用滑动窗口解决问题。

学新通

 如果用暴力解法,时间复杂度为O(n^2),相当于两层遍历。使用哈希表(代码中设置的集合存储不重复元素)只需要遍历一次。

  1.  
    class Solution(object):
  2.  
    def lengthOfLongestSubstring(self, s):
  3.  
    """
  4.  
    :type s: str
  5.  
    :rtype: int
  6.  
    """
  7.  
    hashtable = set()
  8.  
    n = len(s)
  9.  
    # 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
  10.  
    rightFlag, maxLength = -1, 0
  11.  
    for leftFlag in range(n):
  12.  
    if leftFlag != 0:
  13.  
    # 左指针向右移动一格,移除一个字符
  14.  
    # 从集合set中删除数据用remove
  15.  
    hashtable.remove(s[leftFlag - 1])
  16.  
    while rightFlag 1 < n and s[rightFlag 1] not in hashtable:
  17.  
    # 不断地移动右指针
  18.  
    # 向集合set中添加数据用add
  19.  
    hashtable.add(s[rightFlag 1])
  20.  
    rightFlag = 1
  21.  
    # 第 leftFlag 到 rightFlag 个字符是一个极长的无重复字符子串
  22.  
    maxLength = max(maxLength, rightFlag - leftFlag 1)
  23.  
    return maxLength
学新通

12. 整数转罗马数字

学新通

  1.  
    class Solution(object):
  2.  
    def intToRoman(self, num):
  3.  
    """
  4.  
    :type num: int
  5.  
    :rtype: str
  6.  
    """
  7.  
    hashtable = {1000:'M', 900:'CM', 500:'D', 400:'CD',
  8.  
    100:'C', 90:'XC', 50:'L', 40:'XL', 10:'X',
  9.  
    9:'IX', 5:'V', 4:'IV', 1:'I'}
  10.  
    li = ''
  11.  
    while num > 0:
  12.  
    for key in hashtable:
  13.  
    if num - key >= 0:
  14.  
    li = hashtable[key]
  15.  
    num -= key
  16.  
    return li
学新通

理论上是没错的不知道为什么在线结果和离线结果不一样,另一种能通过的解法:

  1.  
    class Solution:
  2.  
     
  3.  
    THOUSANDS = ["", "M", "MM", "MMM"]
  4.  
    HUNDREDS = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"]
  5.  
    TENS = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]
  6.  
    ONES = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]
  7.  
     
  8.  
    def intToRoman(self, num):
  9.  
    return Solution.THOUSANDS[num // 1000] \
  10.  
    Solution.HUNDREDS[num % 1000 // 100] \
  11.  
    Solution.TENS[num % 100 // 10] \
  12.  
    Solution.ONES[num % 10]

17. 电话号码的字母组合

学新通

  1.  
    class Solution(object):
  2.  
    def letterCombinations(self, digits):
  3.  
    """
  4.  
    :type digits: str
  5.  
    :rtype: List[str]
  6.  
    """
  7.  
    if not digits:
  8.  
    return []
  9.  
     
  10.  
    hashtable = {
  11.  
    "2": "abc",
  12.  
    "3": "def",
  13.  
    "4": "ghi",
  14.  
    "5": "jkl",
  15.  
    "6": "mno",
  16.  
    "7": "pqrs",
  17.  
    "8": "tuv",
  18.  
    "9": "wxyz",
  19.  
    }
  20.  
     
  21.  
    def backtrack(index):
  22.  
    if index == len(digits):
  23.  
    res.append("".join(path))
  24.  
    else:
  25.  
    digit = digits[index]
  26.  
    for letter in hashtable[digit]:
  27.  
    path.append(letter)
  28.  
    backtrack(index 1)
  29.  
    path.pop()
  30.  
     
  31.  
    path = []
  32.  
    res = []
  33.  
    backtrack(0)
  34.  
    return res
学新通

 30. 串联所有单词的子串

学新通

学新通

  1.  
    class Solution(object):
  2.  
    def findSubstring(self, s, words):
  3.  
    """
  4.  
    :type s: str
  5.  
    :type words: List[str]
  6.  
    :rtype: List[int]
  7.  
    """
  8.  
    if not words:
  9.  
    return []
  10.  
    singleword_len, s_len = len(words[0]), len(s)
  11.  
    t_len = singleword_len * len(words) # 子串的长度
  12.  
     
  13.  
    word_dict = {} # words的哈希表
  14.  
    for word in words:
  15.  
    word_dict[word] = word_dict.get(word, 0) 1
  16.  
     
  17.  
    ans = []
  18.  
    for offset in range(singleword_len):
  19.  
    lo, lo_max = offset, s_len - t_len
  20.  
    while lo <= lo_max:
  21.  
    tmp_dict = word_dict.copy()
  22.  
    match = True
  23.  
    for hi in range(lo t_len, lo, -singleword_len): # 从尾到头搜索单词
  24.  
    word = s[hi - singleword_len: hi]
  25.  
    if word not in tmp_dict or tmp_dict.get(word, 0) == 0:
  26.  
    match = False
  27.  
    break # 当前单词不符合要求 直接停止这个子串的搜索
  28.  
    tmp_dict[word] -= 1
  29.  
    if match:
  30.  
    ans.append(lo)
  31.  
    lo = hi
  32.  
    return ans
学新通

41. 缺失的第一个正数

学新通

 哈希表的优势在于:哈希表是一个可以支持快速查找的数据结构:给定一个元素,我们可以在 O(1) 的时间查找该元素是否在哈希表中。

学新通

1. 假设列表中的数为[1,n],只检验[1,n]范围内是否有缺失的正整数,因此将负数标记为n 1不干扰最后结果;

2. 遍历列表,当元素在[1,n]范围内时,将nums对应位置更改为负数,起到标记作用(当nums[i]为负数时,说明存在i 1);

3. 返回列表中第一个为正整数的下标 1,如果全都为负数(全都被标记)则返回n 1。

  1.  
    class Solution(object):
  2.  
    def firstMissingPositive(self, nums):
  3.  
    """
  4.  
    :type nums: List[int]
  5.  
    :rtype: int
  6.  
    """
  7.  
    n = len(nums)
  8.  
    for i in range(n):
  9.  
    if nums[i] <= 0:
  10.  
    nums[i] = n 1
  11.  
    for i in range(n):
  12.  
    num = abs(nums[i])
  13.  
    if num <= n:
  14.  
    nums[num - 1] = -abs(nums[num - 1])
  15.  
    for i in range(n):
  16.  
    if nums[i] > 0:
  17.  
    return i 1
  18.  
    return n 1
学新通

49. 字母异位词分组

学新通

  1.  
    class Solution(object):
  2.  
    def groupAnagrams(self, strs):
  3.  
    """
  4.  
    :type strs: List[str]
  5.  
    :rtype: List[List[str]]
  6.  
    """
  7.  
    hashtable = dict()
  8.  
    for i in range(len(strs)):
  9.  
    word = ''.join(sorted(strs[i]))
  10.  
    if word not in hashtable:
  11.  
    hashtable[word] = [strs[i]]
  12.  
    else:
  13.  
    hashtable[word].append(strs[i])
  14.  
    return list(hashtable.values())

 或者用collections库函数defaultdict排序:

  1.  
    import collections
  2.  
    class Solution:
  3.  
    def groupAnagrams(self, strs):
  4.  
    mp = collections.defaultdict(list)
  5.  
     
  6.  
    for st in strs:
  7.  
    key = "".join(sorted(st))
  8.  
    mp[key].append(st)
  9.  
     
  10.  
    return list(mp.values())

73. 矩阵置零

学新通

  1.  
    class Solution(object):
  2.  
    def setZeroes(self, matrix):
  3.  
    """
  4.  
    :type matrix: List[List[int]]
  5.  
    :rtype: None Do not return anything, modify matrix in-place instead.
  6.  
    """
  7.  
    n_row , n_col = len(matrix), len(matrix[0])
  8.  
    row = []
  9.  
    col = []
  10.  
    for i in range(n_row):
  11.  
    for j in range(n_col):
  12.  
    if matrix[i][j] == 0:
  13.  
    row.append(i)
  14.  
    col.append(j)
  15.  
    for val in row:
  16.  
    for j in range(n_col):
  17.  
    matrix[val][j] = 0
  18.  
    for val in col:
  19.  
    for i in range(n_row):
  20.  
    matrix[i][val] = 0
  21.  
    return matrix
学新通

 用矩阵中的第一行和第一列标记该行/列是否有0

76. 最小覆盖子串

学新通

  1.  
    import collections
  2.  
    class Solution(object):
  3.  
    def minWindow(self, s, t):
  4.  
    """
  5.  
    :type s: str
  6.  
    :type t: str
  7.  
    :rtype: str
  8.  
    """
  9.  
    # 创建一个字典表示当前滑动窗口中需要各元素的数量
  10.  
    need = collections.defaultdict(int)
  11.  
    for val in t:
  12.  
    need[val] = 1
  13.  
    needCnt = len(t)
  14.  
    left = 0
  15.  
    res = (0, float('inf'))
  16.  
    for right, val in enumerate(s):
  17.  
    if need[val] > 0:
  18.  
    needCnt -= 1
  19.  
    need[val] -= 1
  20.  
    if needCnt == 0: # 步骤一:滑动窗口包含了所有T元素
  21.  
    while True: # 步骤二:增加i,排除多余元素
  22.  
    val = s[left]
  23.  
    if need[val] == 0: # 多余元素的val小于0
  24.  
    break
  25.  
    need[val] = 1
  26.  
    left = 1
  27.  
    if right - left < res[1] - res[0]: # 记录结果
  28.  
    res = (left, right)
  29.  
    need[s[left]] = 1 # 步骤三:i增加一个位置,寻找新的满足条件滑动窗口
  30.  
    needCnt = 1
  31.  
    left = 1
  32.  
    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. 从前序与中序遍历序列构造二叉树

学新通

学新通

  1.  
    # Definition for a binary tree node.
  2.  
    class TreeNode(object):
  3.  
    def __init__(self, val=0, left=None, right=None):
  4.  
    self.val = val
  5.  
    self.left = left
  6.  
    self.right = right
  7.  
     
  8.  
     
  9.  
    class Solution(object):
  10.  
    def buildTree(self, preorder, inorder):
  11.  
    """
  12.  
    :type preorder: List[int]
  13.  
    :type inorder: List[int]
  14.  
    :rtype: TreeNode
  15.  
    """
  16.  
     
  17.  
    def myBuildTree(preorder_left, preorder_right, inorder_left, inorder_right):
  18.  
    if preorder_left > preorder_right:
  19.  
    return None
  20.  
     
  21.  
    # 前序遍历中的第一个节点就是根节点
  22.  
    preorder_root = preorder_left
  23.  
    # 在中序遍历中定位根节点
  24.  
    inorder_root = index[preorder[preorder_root]]
  25.  
     
  26.  
    # 先把根节点建立出来
  27.  
    root = TreeNode(preorder[preorder_root])
  28.  
    # 得到左子树中的节点数目
  29.  
    size_left_subtree = inorder_root - inorder_left
  30.  
    # 递归地构造左子树,并连接到根节点
  31.  
    # 先序遍历中「从 左边界 1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
  32.  
    root.left = myBuildTree(preorder_left 1, preorder_left size_left_subtree, inorder_left,
  33.  
    inorder_root - 1)
  34.  
    # 递归地构造右子树,并连接到根节点
  35.  
    # 先序遍历中「从 左边界 1 左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位 1 到 右边界」的元素
  36.  
    root.right = myBuildTree(preorder_left size_left_subtree 1, preorder_right, inorder_root 1,
  37.  
    inorder_right)
  38.  
    return root
  39.  
     
  40.  
    n = len(preorder)
  41.  
    # 构造哈希映射,帮助我们快速定位根节点
  42.  
    index = {element: i for i, element in enumerate(inorder)}
  43.  
    return myBuildTree(0, n - 1, 0, n - 1)
学新通

没太看明白,做到二叉树的时候再看一遍吧。

126. 单词接龙

学新通

 第一种方法:建立邻接图,找到所有相邻的点

  1.  
    import collections
  2.  
    class Solution(object):
  3.  
    def findLadders(self, beginWord, endWord, wordList):
  4.  
    """
  5.  
    :type beginWord: str
  6.  
    :type endWord: str
  7.  
    :type wordList: List[str]
  8.  
    :rtype: List[List[str]]
  9.  
    """
  10.  
    m = len(wordList)
  11.  
    # endWord不在wordList中
  12.  
    if endWord not in set(wordList):
  13.  
    return []
  14.  
     
  15.  
    # 判断word1和word2中是否只有一个字母不同
  16.  
    def is_neighbor(word1, word2):
  17.  
    cnt_diff = 0
  18.  
    for i in range(len(word1)):
  19.  
    if word1[i] != word2[i]:
  20.  
    cnt_diff = 1
  21.  
    if cnt_diff > 1:
  22.  
    return False
  23.  
    return True
  24.  
     
  25.  
    # 建立邻接图
  26.  
    neighbors = collections.defaultdict(list)
  27.  
    # neighbors[i]存储与word[i]有一个字母不同的word的下标
  28.  
    for i in range(m):
  29.  
    for j in range(i 1, m):
  30.  
    if is_neighbor(wordList[i], wordList[j]):
  31.  
    neighbors[i].append(j)
  32.  
    neighbors[j].append(i)
  33.  
     
  34.  
    # 找出beginWord的所有邻居
  35.  
    begins = [i for i in range(m) if is_neighbor(beginWord, wordList[i])]
  36.  
    # beginWord没有邻居
  37.  
    if len(begins) == 0:
  38.  
    return False
  39.  
    # 广度优先找最短路径
  40.  
    all_paths = [] # 存储可到达路径
  41.  
    que = [[pos] for pos in begins] # 存储未到达路径
  42.  
    visited = set() # 存储所有走过的点
  43.  
    while que:
  44.  
    visited_add = set() # 存储当前step走过的点
  45.  
    for _ in range(len(que)):
  46.  
    cur_path = que.pop(0)
  47.  
    cur_pos = cur_path[-1]
  48.  
    if wordList[cur_pos] == endWord:
  49.  
    all_paths.append(cur_path)
  50.  
    else:
  51.  
    for next_pos in neighbors[cur_pos]:
  52.  
    # 如果在之前的step中遍历过next_pos则没必要再走
  53.  
    if next_pos in visited:
  54.  
    continue
  55.  
    que.append(cur_path [next_pos])
  56.  
    visited_add.add(next_pos)
  57.  
    visited = visited | visited_add
  58.  
    if len(all_paths) > 0:
  59.  
    break
  60.  
    all_paths_words = [[beginWord] [wordList[i] for i in l] for l in all_paths]
  61.  
    return all_paths_words
学新通

第二种方法:优化的BFS(广度优先搜索)

1. 把每个单词看作可以hash成c个值,c代表单词长度,比如单词'hit',可以hash成'*it','h*t','hi*'这3个值,如果2个单词的hash值有重合,说明这2个单词是可以通过改1个字母变成另外一个;

2. 双向BFS:分别从beginWord和endWord开始遍历,当哪边的节点少就遍历哪边,当两边的节点存在交集时,遍历结束。如果一直没交集,任何一头走到末尾就可以结束,代表全部节点都遍历完成。

  1.  
    import collections
  2.  
     
  3.  
     
  4.  
    class Solution:
  5.  
    def findLadders(self, beginWord, endWord, wordList):
  6.  
    if endWord not in wordList:
  7.  
    return []
  8.  
    hashtable = collections.defaultdict(list)
  9.  
    # hashtable存储格式符合要求的单词(h*t:[hot])
  10.  
    for word in wordList:
  11.  
    for i in range(len(word)):
  12.  
    hashtable[word[:i] "*" word[i 1:]].append(word)
  13.  
     
  14.  
    def findNeighbor(word):
  15.  
    for i in range(len(word)):
  16.  
    # 遍历符合格式条件的单词(符合格式条件说明newWord是word的neighbor)
  17.  
    for newWord in hashtable[word[:i] '*' word[i 1:]]:
  18.  
    if newWord not in visited:
  19.  
    yield newWord
  20.  
     
  21.  
    def findPath(end):
  22.  
    res = []
  23.  
    for curr in end:
  24.  
    # key存放右面的word,value存放左面的word,已知右面的word找它左面所有word
  25.  
    for parent in path[curr[0]]:
  26.  
    res.append([parent] curr)
  27.  
    return res
  28.  
     
  29.  
    visited = set()
  30.  
    path = collections.defaultdict(set)
  31.  
    begin = {beginWord} # 记录最后一层所有节点(上一次循环的visited_add)
  32.  
    end = {endWord}
  33.  
    forward = True # forward为True时从begin开始遍历,False从end开始遍历
  34.  
    while begin and end:
  35.  
    if len(begin) > len(end):
  36.  
    # 从节点少的一端开始遍历,begin一端节点多时头尾互换
  37.  
    begin, end = end, begin
  38.  
    forward = not forward
  39.  
    visited_add = set() # 该层的访问过的集合
  40.  
    for word in begin:
  41.  
    visited.add(word)
  42.  
    # 先把begin一端所有节点添加到visited防止重复遍历
  43.  
    for word in begin:
  44.  
    for next_word in findNeighbor(word):
  45.  
    visited_add.add(next_word)
  46.  
    if forward:
  47.  
    # 如果从begin一端开始遍历的,key存放右面的word,value存放左面的word
  48.  
    path[next_word].add(word)
  49.  
    else:
  50.  
    # 如果从end一端开始遍历的,key存放右面的word,value存放左面的word
  51.  
    path[word].add(next_word)
  52.  
    begin = visited_add
  53.  
    if begin & end: # 当begin和end存在交集
  54.  
    res = [[endWord]]
  55.  
    while res[0][0] != beginWord:
  56.  
    res = findPath(res)
  57.  
    return res
  58.  
    return []
学新通

学新通

127. 单词接龙

学新通

  1.  
    import collections
  2.  
    class Solution:
  3.  
    def ladderLength(self, beginWord, endWord, wordList):
  4.  
    wordSet = set(wordList) # 将wordList转为set格式会快很多
  5.  
    if endWord not in wordSet:
  6.  
    return 0
  7.  
     
  8.  
    l_queue = [beginWord]
  9.  
    r_queue = [endWord]
  10.  
    l_visited = {beginWord} # 初始化为集合set
  11.  
    r_visited = {endWord}
  12.  
    depth = 1
  13.  
     
  14.  
    neighbors = collections.defaultdict(list)
  15.  
    for word in wordSet:
  16.  
    for i in range(len(word)):
  17.  
    neighbors[word[:i] '*' word[i 1:]].append(word)
  18.  
     
  19.  
    while l_queue and r_queue:
  20.  
    if len(l_queue) > len(r_queue):
  21.  
    # 每次都走节点少的一端
  22.  
    l_queue, r_queue = r_queue, l_queue
  23.  
    l_visited, r_visited = r_visited, l_visited
  24.  
    for _ in range(len(l_queue)): # 每层开始遍历
  25.  
    cur = l_queue.pop(0)
  26.  
    if cur in r_visited: # 当前节点在另一端遍历过
  27.  
    return depth
  28.  
    for i in range(len(beginWord)):
  29.  
    for nextWord in neighbors[cur[:i] '*' cur[i 1:]]:
  30.  
    if nextWord not in l_visited:
  31.  
    l_queue.append(nextWord)
  32.  
    l_visited.add(nextWord)
  33.  
    depth = 1
  34.  
    return 0
学新通

128. 最长连续序列

学新通

  1.  
    class Solution(object):
  2.  
    def longestConsecutive(self, nums):
  3.  
    """
  4.  
    :type nums: List[int]
  5.  
    :rtype: int
  6.  
    """
  7.  
    hashtable = {}
  8.  
    maxlength = 0
  9.  
    for num in nums:
  10.  
    if num not in hashtable:
  11.  
    left = hashtable.get(num-1, 0)
  12.  
    right = hashtable.get(num 1, 0)
  13.  
     
  14.  
    currentlength = right left 1
  15.  
    maxlength = max(maxlength, currentlength)
  16.  
     
  17.  
    hashtable[num] = currentlength
  18.  
    hashtable[num right] = currentlength
  19.  
    hashtable[num - left] = currentlength
  20.  
     
  21.  
    return maxlength
学新通

right记录在num右边还有几个连续的数,left记录num左边还有几个连续的数,每个num循环结束更新两边端点的currentlength值,记录当前最长长度。

dict.get(key, n) 返回字典中key对应的value,如果不存在则返回n。

或者第二种思路:

  1.  
    import collections
  2.  
    class Solution(object):
  3.  
    def longestConsecutive(self, nums):
  4.  
    """
  5.  
    :type nums: List[int]
  6.  
    :rtype: int
  7.  
    """
  8.  
    if not nums:
  9.  
    return 0
  10.  
    # 返回key为元素,value为元素个数的字典
  11.  
    cnts = collections.Counter(nums)
  12.  
    maxlength = 1
  13.  
    for num in cnts:
  14.  
    # 当num-1不在字典中,说明num是一串连续数的开始
  15.  
    if num - 1 in cnts:
  16.  
    continue
  17.  
    else:
  18.  
    currentlength = 0
  19.  
    next = num
  20.  
    while next in cnts:
  21.  
    currentlength = 1
  22.  
    next = 1
  23.  
    maxlength = max(maxlength, currentlength)
  24.  
    return maxlength
学新通

 当num-1不在字典中时(即num为一串连续数的开始)检查该串连续数的长度,其中cnts是记录nums中元素种类和个数的字典。

学新通

138. 复制带随机指针的列表

学新通

学新通

学新通

学新通

  1.  
    class Node:
  2.  
    def __init__(self, x, next=None, random=None):
  3.  
    self.val = int(x)
  4.  
    self.next = next
  5.  
    self.random = random
  6.  
     
  7.  
     
  8.  
    class Solution(object):
  9.  
    def copyRandomList(self, head):
  10.  
    """
  11.  
    :type head: Node
  12.  
    :rtype: Node
  13.  
    """
  14.  
    if not head:
  15.  
    return None
  16.  
    # 创建一个哈希表,key是原节点,value是新节点
  17.  
    hashtable = dict()
  18.  
    p = head
  19.  
    while p:
  20.  
    new_node = Node(p.val, None, None)
  21.  
    hashtable[p] = new_node
  22.  
    p = p.next
  23.  
    p = head
  24.  
    while p:
  25.  
    if p.next:
  26.  
    hashtable[p].next = hashtable[p.next]
  27.  
    if p.random:
  28.  
    hashtable[p].random = hashtable[p.random]
  29.  
    p = p.next
  30.  
    return hashtable[head]
学新通

139. 单词拆分

学新通

学新通

  1.  
    class Solution(object):
  2.  
    def wordBreak(self, s, wordDict):
  3.  
    """
  4.  
    :type s: str
  5.  
    :type wordDict: List[str]
  6.  
    :rtype: bool
  7.  
    """
  8.  
    s_len = len(s)
  9.  
    dp = [False for _ in range(s_len 1)]
  10.  
    dp[0] = True
  11.  
    for i in range(s_len):
  12.  
    # i为开始索引,j为结束索引
  13.  
    for j in range(i 1, s_len 1):
  14.  
    if dp[i] == True and s[i:j] in wordDict:
  15.  
    dp[j] = True
  16.  
     
  17.  
    return dp[s_len]
学新通

另外一种方法采用回溯思想:

学新通

 在如下样例中存在大量重复计算情况下可能会超时:

学新通

使用一个数组存储计算结果,数组索引为指针位置(以该指针作为起始位置的计算结果),值为计算的结果,下次遇到相同的子问题直接返回数组中的缓存值:

学新通

  1.  
    class Solution(object):
  2.  
    def wordBreak(self, s, wordDict):
  3.  
    """
  4.  
    :type s: str
  5.  
    :type wordDict: List[str]
  6.  
    :rtype: bool
  7.  
    """
  8.  
    memory = {}
  9.  
     
  10.  
    def dfs(s, cur):
  11.  
    # 如果已经算过了,直接返回
  12.  
    if cur in memory:
  13.  
    return memory[cur]
  14.  
    # 拆分完成
  15.  
    if cur == len(s):
  16.  
    memory[cur] = True
  17.  
    return True
  18.  
     
  19.  
    for word in wordDict:
  20.  
    # 如果当前的单词比剩下的字符串还要长,则不必查了以防越界
  21.  
    if len(word) > len(s) - cur:
  22.  
    continue
  23.  
    # 如果下一个单词能成功拆分且按这样拆下去能成功则返回True
  24.  
    elif s[cur:cur len(word)] == word and dfs(s, cur len(word)):
  25.  
    memory[cur] = True
  26.  
    return True
  27.  
    # 没有一种情况拆分成功
  28.  
    memory[cur] = False
  29.  
    return False
  30.  
     
  31.  
    return dfs(s, 0)
学新通

140. 单词拆分Ⅱ

学新通

 动态规划:

  1.  
    from functools import lru_cache
  2.  
    class Solution(object):
  3.  
    def wordBreak(self, s, wordDict):
  4.  
    """
  5.  
    :type s: str
  6.  
    :type wordDict: List[str]
  7.  
    :rtype: List[str]
  8.  
    """
  9.  
     
  10.  
    @lru_cache(None)
  11.  
    def backtrack(index) :
  12.  
    if index == len(s):
  13.  
    return [[]]
  14.  
    ans = list()
  15.  
    for i in range(index 1, len(s) 1):
  16.  
    word = s[index:i]
  17.  
    if word in wordSet:
  18.  
    nextWordBreaks = backtrack(i)
  19.  
    for nextWordBreak in nextWordBreaks:
  20.  
    ans.append(nextWordBreak.copy() [word])
  21.  
    return ans
  22.  
     
  23.  
    wordSet = set(wordDict)
  24.  
    breakList = backtrack(0)
  25.  
    return [" ".join(words[::-1]) for words in breakList]
学新通

@Iru_cache(None) 在第二次调用函数时不进行计算直接返回缓存结果。

第二种方法,使用memo[i:]代表s[i:]这个字符串能否被分割,如果不能停止后续遍历。(其实和第一种是一样的但是第一种在python3里运行)

  1.  
    class Solution:
  2.  
    # 带备忘录的记忆化搜索
  3.  
    def wordBreak(self, s, wordDict):
  4.  
    res = []
  5.  
    memo = [1] * (len(s) 1)
  6.  
    wordDict = set(wordDict)
  7.  
     
  8.  
    def dfs(wordDict, temp, pos):
  9.  
    num = len(res) # 回溯前先记下答案中有多少个元素
  10.  
    if pos == len(s):
  11.  
    res.append(" ".join(temp))
  12.  
    return
  13.  
    for i in range(pos, len(s) 1):
  14.  
    if memo[i] and s[pos:i] in wordDict: # 添加备忘录的判断条件
  15.  
    temp.append(s[pos:i])
  16.  
    dfs(wordDict, temp, i)
  17.  
    temp.pop()
  18.  
    # 答案中的元素没有增加,说明s[pos:]不能分割,修改备忘录
  19.  
    memo[pos] = 1 if len(res) > num else 0
  20.  
     
  21.  
    dfs(wordDict, [], 0)
  22.  
    return res
学新通

141. 环形链表

学新通

  1.  
    # Definition for singly-linked list.
  2.  
    # class ListNode(object):
  3.  
    # def __init__(self, x):
  4.  
    # self.val = x
  5.  
    # self.next = None
  6.  
     
  7.  
    class Solution(object):
  8.  
    def hasCycle(self, head):
  9.  
    """
  10.  
    :type head: ListNode
  11.  
    :rtype: bool
  12.  
    """
  13.  
    if not head:
  14.  
    return False
  15.  
    hash = set()
  16.  
    hash.add(head)
  17.  
    now = head
  18.  
    while now.next:
  19.  
    now = now.next
  20.  
    if now in hash:
  21.  
    return True
  22.  
    else:
  23.  
    hash.add(now)
  24.  
    return False
学新通

学新通

  1.  
    # Definition for singly-linked list.
  2.  
    class ListNode(object):
  3.  
    def __init__(self, x):
  4.  
    self.val = x
  5.  
    self.next = None
  6.  
     
  7.  
    class Solution(object):
  8.  
    def hasCycle(self, head):
  9.  
    """
  10.  
    :type head: ListNode
  11.  
    :rtype: bool
  12.  
    """
  13.  
    if not head or not head.next:
  14.  
    return False
  15.  
     
  16.  
    slow = head
  17.  
    fast = head.next
  18.  
     
  19.  
    while slow != fast:
  20.  
    if not fast or not fast.next:
  21.  
    return False
  22.  
    slow = slow.next
  23.  
    fast = fast.next.next
  24.  
     
  25.  
    return True
学新通

142. 环形链表Ⅱ

学新通

  1.  
    # Definition for singly-linked list.
  2.  
    # class ListNode(object):
  3.  
    # def __init__(self, x):
  4.  
    # self.val = x
  5.  
    # self.next = None
  6.  
     
  7.  
    class Solution(object):
  8.  
    def detectCycle(self, head):
  9.  
    """
  10.  
    :type head: ListNode
  11.  
    :rtype: ListNode
  12.  
    """
  13.  
    fast, slow = head, head
  14.  
    while True:
  15.  
    if not (fast and fast.next): return
  16.  
    fast, slow = fast.next.next, slow.next
  17.  
    if fast == slow: break
  18.  
    fast = head
  19.  
    while fast != slow:
  20.  
    fast, slow = fast.next, slow.next
  21.  
    return fast
学新通

构建两次相遇:

学新通

 149. 直线上最多的点数

学新通

  1.  
    class Solution(object):
  2.  
    def maxPoints(self, points):
  3.  
    """
  4.  
    :type points: List[List[int]]
  5.  
    :rtype: int
  6.  
    """
  7.  
    n = len(points)
  8.  
    if n <= 2:
  9.  
    return n
  10.  
    res = 2
  11.  
    for i in range(n):
  12.  
    x1, y1 = points[i][0], points[i][1]
  13.  
    has = {}
  14.  
    for j in range(i 1, n):
  15.  
    x2, y2 = points[j][0], points[j][1]
  16.  
    if x1 == x2:
  17.  
    a, b, c = 1, 0, -x1
  18.  
    elif y1 == y2:
  19.  
    a, b, c = 0, 1, -y1
  20.  
    else:
  21.  
    a = 1.0
  22.  
    b = 1.0 * (x1 - x2) / (y2 - y1)
  23.  
    c = 1.0 * (x1 * y2 - x2 * y1) / (y1 - y2)
  24.  
    if (a,b,c) in has.keys():
  25.  
    has[(a,b,c)] =1
  26.  
    res = max(res,has[(a,b,c)])
  27.  
    else:
  28.  
    has[(a,b,c)] = 2
  29.  
    return res
学新通

 字典中的key可以使用任意不可变数据(整数、实数、复数、字符串、元组等可散列数据),不能使用可变类型(列表、集合、字典)。

160. 相交链表

学新通

  1.  
    # Definition for singly-linked list.
  2.  
    # class ListNode(object):
  3.  
    # def __init__(self, x):
  4.  
    # self.val = x
  5.  
    # self.next = None
  6.  
     
  7.  
    class Solution(object):
  8.  
    def getIntersectionNode(self, headA, headB):
  9.  
    """
  10.  
    :type head1, head1: ListNode
  11.  
    :rtype: ListNode
  12.  
    """
  13.  
    nowA, nowB = headA, headB
  14.  
    while nowA != nowB:
  15.  
    nowA = nowA.next if nowA else headB
  16.  
    nowB = nowB.next if nowB else headA
  17.  
    return nowA
学新通

两个指针分别从两个链表的head开始遍历,当其中一个遍历完之后从另一个链表的head继续遍历。

学新通学新通

 166. 分数到小数

学新通

  1.  
    import collections
  2.  
    class Solution(object):
  3.  
    def fractionToDecimal(self, numerator, denominator):
  4.  
    """
  5.  
    :type numerator: int
  6.  
    :type denominator: int
  7.  
    :rtype: str
  8.  
    """
  9.  
    ans = []
  10.  
    flag = True # 标记结果的正负
  11.  
    if numerator == 0:
  12.  
    return '0'
  13.  
    if (numerator < 0 and denominator > 0) or (numerator > 0 and denominator < 0):
  14.  
    # if (numerator < 0) != (denominator < 0):
  15.  
    flag = False
  16.  
    numerator = abs(numerator)
  17.  
    denominator = abs(denominator)
  18.  
    if not flag:
  19.  
    ans.append('-')
  20.  
     
  21.  
    # 整数部分
  22.  
    tmp = numerator // denominator
  23.  
    ans.append('%d' % tmp)
  24.  
    # ans.append(str(tmp))
  25.  
    remainder = numerator % denominator
  26.  
    if remainder == 0:
  27.  
    return ''.join(ans)
  28.  
    ans.append('.')
  29.  
     
  30.  
    # 小数部分
  31.  
    hashtable = collections.defaultdict(int) # 记录余数第一次出现的下标
  32.  
    while remainder and remainder not in hashtable:
  33.  
    hashtable[remainder] = len(ans)
  34.  
    remainder *= 10
  35.  
    ans.append('%d' % (remainder // denominator))
  36.  
    remainder %= denominator
  37.  
    if remainder:
  38.  
    start = hashtable[remainder]
  39.  
    ans.insert(start, '(')
  40.  
    ans.append(')')
  41.  
     
  42.  
    return ''.join(ans)
学新通

187. 重复的DNA序列

学新通

  1.  
    import collections
  2.  
    class Solution(object):
  3.  
    def findRepeatedDnaSequences(self, s):
  4.  
    """
  5.  
    :type s: str
  6.  
    :rtype: List[str]
  7.  
    """
  8.  
    hashtable = collections.defaultdict(int)
  9.  
    if len(s) < 10:
  10.  
    return []
  11.  
    for left in range(len(s)-9):
  12.  
    dna = s[left:left 10]
  13.  
    hashtable[str(dna)] = 1
  14.  
    ans = []
  15.  
    for key in hashtable:
  16.  
    if hashtable[key] > 1:
  17.  
    ans.append(key)
  18.  
    return ans
学新通

 202. 快乐数

学新通

  1.  
    class Solution(object):
  2.  
    def isHappy(self, n):
  3.  
    """
  4.  
    :type n: int
  5.  
    :rtype: bool
  6.  
    """
  7.  
    hashtable = set()
  8.  
    tmp = n
  9.  
    while tmp not in hashtable and tmp != 1:
  10.  
    hashtable.add(tmp)
  11.  
    num = []
  12.  
    count = len(str(tmp)) # 数字位数
  13.  
    for i in range(count-1, -1, -1):
  14.  
    num.append(tmp // (10 ** i))
  15.  
    tmp = tmp % (10 ** i)
  16.  
    tmp = 0
  17.  
    for i in range(len(num)):
  18.  
    tmp = num[i] ** 2
  19.  
     
  20.  
    if tmp != 1:
  21.  
    return False
  22.  
    else:
  23.  
    return True
学新通

或者使用快慢指针,指针相遇时返回False

205. 同构字符串

学新通

  1.  
    import collections
  2.  
    class Solution(object):
  3.  
    def isIsomorphic(self, s, t):
  4.  
    """
  5.  
    :type s: str
  6.  
    :type t: str
  7.  
    :rtype: bool
  8.  
    """
  9.  
    hashtable_st = collections.defaultdict(str) # 记录s到t的映射关系
  10.  
    hashtable_ts = set() # 检验是否有相同两个字符映射到同一字符
  11.  
     
  12.  
    for i, letter in enumerate(s):
  13.  
    if letter not in hashtable_st:
  14.  
    if t[i] not in hashtable_ts:
  15.  
    hashtable_st[letter] = t[i]
  16.  
    hashtable_ts.add(t[i])
  17.  
    else:
  18.  
    return False
  19.  
    else:
  20.  
    if hashtable_st[letter] != t[i]:
  21.  
    return False
  22.  
    return True
学新通

229. 求众数

学新通

  1.  
    import collections
  2.  
    class Solution(object):
  3.  
    def majorityElement(self, nums):
  4.  
    """
  5.  
    :type nums: List[int]
  6.  
    :rtype: List[int]
  7.  
    """
  8.  
    hashtable = collections.defaultdict(int)
  9.  
    ans = []
  10.  
    for num in nums:
  11.  
    hashtable[num] = 1
  12.  
    for num in hashtable:
  13.  
    if hashtable[num] > len(nums) // 3:
  14.  
    ans.append(num)
  15.  
    return ans
学新通

242. 有效的字母异位词

学新通

  1.  
    class Solution(object):
  2.  
    def isAnagram(self, s, t):
  3.  
    """
  4.  
    :type s: str
  5.  
    :type t: str
  6.  
    :rtype: bool
  7.  
    """
  8.  
    hashtable_s = collections.defaultdict(int)
  9.  
    hashtable_t = collections.defaultdict(int)
  10.  
     
  11.  
    for letter in s:
  12.  
    hashtable_s[letter] = 1
  13.  
    for letter in t:
  14.  
    hashtable_t[letter] = 1
  15.  
    return hashtable_s == hashtable_t
学新通

264. 丑数

学新通

  1.  
    import heapq
  2.  
    class Solution(object):
  3.  
    def nthUglyNumber(self, n):
  4.  
    """
  5.  
    :type n: int
  6.  
    :rtype: int
  7.  
    """
  8.  
    factors = [2, 3, 5]
  9.  
    visited = {1} # 遍历过的丑数集合
  10.  
    heap = [1] # 堆顶
  11.  
     
  12.  
    for i in range(n - 1):
  13.  
    curr = heapq.heappop(heap) # 从堆中弹出最小值
  14.  
    for factor in factors:
  15.  
    if (curr * factor) not in visited:
  16.  
    nxt = curr * factor
  17.  
    visited.add(nxt)
  18.  
    heapq.heappush(heap, nxt) # 往堆中插入新的值
  19.  
     
  20.  
    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),并将对应指针加一。

  1.  
    class Solution(object):
  2.  
    def nthUglyNumber(self, n):
  3.  
    """
  4.  
    :type n: int
  5.  
    :rtype: int
  6.  
    """
  7.  
    ans = [0, 1]
  8.  
    p2 = p3 = p5 = 1
  9.  
     
  10.  
    for i in range(2, n 1):
  11.  
    ans.append(min(ans[p2] * 2, ans[p3] * 3, ans[p5] * 5))
  12.  
    if ans[i] == ans[p2] * 2:
  13.  
    p2 = 1
  14.  
    if ans[i] == ans[p3] * 3:
  15.  
    p3 = 1
  16.  
    if ans[i] == ans[p5] * 5:
  17.  
    p5 = 1
  18.  
     
  19.  
    return ans
学新通

313. 超级丑数

学新通

  1.  
    class Solution(object):
  2.  
    def nthSuperUglyNumber(self, n, primes):
  3.  
    """
  4.  
    :type n: int
  5.  
    :type primes: List[int]
  6.  
    :rtype: int
  7.  
    """
  8.  
    len_primes = len(primes)
  9.  
    pointers = [1] * (len_primes)
  10.  
    num = [0] * (len_primes)
  11.  
    ans = [0, 1]
  12.  
     
  13.  
    for i in range(2, n 1):
  14.  
    for j in range(len_primes):
  15.  
    num[j] = ans[pointers[j]] * primes[j]
  16.  
    next = min(num)
  17.  
    for j in range(len_primes):
  18.  
    if num[j] == next:
  19.  
    pointers[j] = 1
  20.  
    ans.append(next)
  21.  
     
  22.  
    return ans[n]
学新通

这种方法超出时间限制了,可以通过以下方法优化(减少一个for循环):

  1.  
    class Solution(object):
  2.  
    def nthSuperUglyNumber(self, n, primes):
  3.  
    """
  4.  
    :type n: int
  5.  
    :type primes: List[int]
  6.  
    :rtype: int
  7.  
    """
  8.  
    ans = [0] * (n 1)
  9.  
    m = len(primes)
  10.  
    pointers = [0] * m
  11.  
    num = [1] * m
  12.  
     
  13.  
    for i in range(1, n 1):
  14.  
    next = min(num)
  15.  
    ans[i] = next
  16.  
    for j in range(m):
  17.  
    if num[j] == next:
  18.  
    pointers[j] = 1
  19.  
    num[j] = ans[pointers[j]] * primes[j]
  20.  
    return ans[n]
学新通

或者采用堆排序:

  1.  
    import heapq
  2.  
     
  3.  
     
  4.  
    class Solution(object):
  5.  
    def nthSuperUglyNumber(self, n, primes):
  6.  
    """
  7.  
    :type n: int
  8.  
    :type primes: List[int]
  9.  
    :rtype: int
  10.  
    """
  11.  
    # ans[i] 代表第i 1个丑数
  12.  
    ans = [1] * n
  13.  
    # 丑数, 刚刚乘过的丑数的坐标, 质因数
  14.  
    pq = [(p, 0, i) for i, p in enumerate(primes)]
  15.  
     
  16.  
    for i in range(1, n):
  17.  
    # 目前最新的最小的丑数
  18.  
    ans[i] = pq[0][0]
  19.  
    # 所有等于这个值的要全部出队列,并根据该乘的丑数重新加入队列
  20.  
    while pq and pq[0][0] == ans[i]:
  21.  
    _, idx, p = heapq.heappop(pq)
  22.  
    heapq.heappush(pq, (ans[idx 1] * primes[p], idx 1, p))
  23.  
    return ans[-1]
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgaghkf
系列文章
更多 icon
同类精品
更多 icon
继续加载