leetcode-哈希表

哈希表(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. 两数之和

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]
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值