Leetcode学习笔记 哈希表

哈希表-17/20

设计哈希表-0/2
设计哈希集合
设计哈希映射

哈希集合应用-4

存在重复元素,简单
用set秒杀

只出现一次的数字,简单
用set秒杀,单空间复杂度是O(n)
方法二:异或运算空间复杂度O(1),相同的数异或得0,所有数异或得答案

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        ans = 0
        for item in nums:
            ans ^= item
        return ans

两个数组的交集,简单
直接内置秒杀

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        set1 = set(nums1)
        set2 = set(nums2)
        return set1&set2
        #或者写
        return set.intersection(set1,set2)

快乐数,简单
因为可能会无限循环,所以用一个set存所有已经出现过的迭代结果,如果出现重复则证明无限循环了

class Solution:
    def trans(self,n):
        s = str(n)
        lst = list(s)
        ans = 0
        for item in lst:
            ans += int(item)**2
        return ans
    def isHappy(self, n: int) -> bool:
        numset = set()
        while n not in numset and n != 1:
            numset.add(n)
            n = self.trans(n)
        return n == 1

方法二:用快慢指针检测,如果快指针先到1了,则true,如果快慢指针相遇,则false

哈希映射应用-6
其实就是字典

两数之和,简单
先排序,再用双指针,需要用字典记住排序之前的下标(如果列表里有重复数字怎么办?)
官解:
查 target - item 是否已经在字典里,如果在,返回这两个下标,如果不在,把 item 加入字典

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        dic = {}
        for i ,item in enumerate(nums):
            if target - item in dic:
                return [dic[target - item],i]
            else:
                dic[item] = i

同构字符串,简单
用哈希表对应 s 和 t 的字符,要注意,“ab” “bb”也是不对的,所以要用一个 set 来检查 t 里的字符有没有重复使用

class Solution:
    def isIsomorphic(self, s: str, t: str) -> bool:
        if len(s) != len(t):return False
        n = len(s)
        dic = {}
        used = set()
        for i in range(n):
            if s[i] not in dic:
                if t[i] not in used:#加上了这个判断
                    dic[s[i]] = t[i]
                    used.add(t[i])
                else:
                    return False
            else:
                if dic[s[i]] != t[i]:
                    return False
        return True

两个列表的最小索引总和,简单
用minsum存最小索引和,ans列表存答案的字符串

class Solution:
    def findRestaurant(self, list1: List[str], list2: List[str]) -> List[str]:
        minsum = 2000
        set2 = set(list2)
        for i,item in enumerate(list1):
            if item in list2:#这里如果用in set2用时会短一些
                temp = i + list2.index(item)
                if temp < minsum:
                    minsum = temp
                    ans = [item]
                elif temp == minsum:
                    ans.append(item)
        return ans

字符串中的第一个唯一字符,简单
遍历两遍s,第一遍修改字典,第二遍查找字典键对应的值是否等于1
字典,键位字符,值为出现次数

class Solution:
    def firstUniqChar(self, s: str) -> int:
        dic = {}
        n = len(s)
        for item in s:
            if item not in dic:
                dic[item] = 1
            else:
                dic[item] += 1
        for i in range(n):
            if dic[s[i]] == 1:
                return i
        return -1

两个数的交集II,简单
遍历两遍字典,用字典记录列表1或2中数字在两个列表之中的个数,取min

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
    	if len(nums1) > len(nums2):#加上这句可以优化空间
            return self.intersect(nums2,nums1)
        dic = {}
        ans = []
        for item in nums1:
            if item not in dic:
                dic[item] = [1,0]
            else:
                dic[item][0] += 1
        for item in nums2:
            if item in dic:
                dic[item][1] += 1
        for item in dic:
            k = min(dic[item][0],dic[item][1])
            for _ in range(k):
                ans.append(item)
        return ans

存在重复元素II,简单
暴力超时
哈希解法一:字典键值对为 “数字:最近下标”,检测到重复数字时,如果当前下标与最近下标超过3,则更新最近下标,否则返回True
哈希解法二:维护一个大小为 k 的散列表,在遍历时,如果在散列表中查到,则true。但是Python字典内的存储顺序是随机的,所以没法通过字典操作删除k个item中加入字典最早的。
法一代码:

class Solution:
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
        dic = {}
        for i,item in enumerate(nums):
            if item not in dic:
                dic[item] = i
            else:
                if i - dic[item] > k:
                    dic[item] = i
                else:
                    return True
        return False

设计键-3

字母异位词分组,中等
注意:用sorted排序字符串会返回排序后的字符列表,需要join链接成字符串

class Solution:
    def sortstr(self,s):
        lst = sorted(s)
        return ''.join(lst)
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        count = -1
        ans = []
        dic = {}
        for item in strs:
            temp = self.sortstr(item)
            if temp not in dic:
                count += 1
                dic[temp] = count
                ans.append([item])
            else:
                ans[dic[temp]].append(item)
        return ans

有效的数独,中等
给每行每列每3x3小块都设置一个set
小块的编号 = 3*(行号//3) + (列号//3)
记得把 ‘.’ 跳过

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        rowset = [set() for _ in range(9)]
        colset = [set() for _ in range(9)]
        subset = [set() for _ in range(9)]
        for i in range(9):
            for j in range(9):
                subnum = 3*(i//3) + j//3
                num = board[i][j]
                if num == '.':
                    continue
                if num not in rowset[i] and num not in colset[j] and num not in subset[subnum]:
                    rowset[i].add(num)
                    colset[j].add(num)
                    subset[subnum].add(num)
                else:
                    return False
        return True

寻找重复的子树,中等
官解:用一个三元组来唯一标识一个子树 (node.val , left id , right id ),相同的 id 表示子树相同
则叶节点的 id 为 ( node.val , None , None )

直接将三元组作为key,count作为值的运行时间
远大于
先将三元组映射到 UID = int ,再用UID 索引 count
故使用了两个字典,一个字典将 三元组映射给一个与之对应的独一无二的 uid , 另一个字典用 uid 作为 key 来维护其出现的次数

class Solution:
    def findDuplicateSubtrees(self, root: TreeNode) -> List[TreeNode]:
        trees = {}
        count = {}
        ans = []
        def getuid(node):
            if node:
                tempkey = (node.val,getuid(node.left),getuid(node.right))
                if tempkey not in trees:
                    trees[tempkey] = len(trees)
                    #用已经加入字典的个数来作为 uid,保证了uid 的唯一性
                uid = trees[tempkey]
                if uid not in count:
                    count[uid] = 1
                else:
                    count[uid] += 1
                if count[uid] == 2:
                    ans.append(node)
                return uid
        getuid(root)
        return ans

小结-4/5

宝石与石头,简单
把 J 哈希,然后遍历 S
Python中 in 的时间复杂度:set、dic 为 O(1),list 为 O(n)

class Solution:
    def numJewelsInStones(self, J: str, S: str) -> int:
        jewset = set(J)
        count = 0
        for item in S:
            if item in jewset:#这两句在LeetCode里运行速度其实差不太多,波动都很大
            #if item in J:
                count += 1
        return count

无重复字符的最长子串,中等
跟官解思路一致:双指针法,用一个 set 来存当前无重复字符串里的字符
右指针先遍历,遍历到set里没有的,加入set,
遍历到有的,左指针从当前位置开始遍历到重复元素上次出现的位置,将路上的元素删除
遍历的过程中计算当前无重复字符串的长度
注意:先添加/移除元素,再修改 left 和 right 的值

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if not s:return 0
        uniq = set()
        n = len(s)
        ans ,tempans ,left ,right = 0, 0, 0, 0
        while right < n:
            if s[right] not in uniq:
                #right += 1
                uniq.add(s[right])
                right += 1
                tempans += 1
                #把这一句判断移到else后面,可以减少比较次数,但是需要在所有遍历结束后补上一次判断
                if tempans > ans:
                    ans = tempans
            else:
                while s[left] != s[right]:
                    #left += 1
                    uniq.remove(s[left])
                    left += 1
                    tempans -= 1
                left += 1 #这一句漏了
                right += 1
        return ans

四数相加 II,中等
分成两组处理,遍历AB,将 A[i] + B[j] 出现的次数存入以 A[i] + B[j] 为键的字典
遍历CD,在dic中检索 -(C[i] + D[j]) ,如果找到了,则答案 += dic[ -(C[i] + D[j])]

class Solution:
    def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:
        dic = {}
        n = len(A)
        count = 0
        for i in range(n):
            for j in range(n):
                num = A[i] + B[j]
                if num in dic:
                    dic[num] += 1
                else:
                    dic[num] = 1
        for i in range(n):
            for j in range(n):
                num = C[i] + D[j]
                if -num in dic:
                    count += dic[-num]
        return count

前 K 个高频元素,中等
用哈希表统计每个元素出现的次数,O(n)
直接对次数排序,O(nlogn),不符合要求
维护一个 k 个元素的最小堆,O(nlogk),符合要求

在这里插入代码片

常数时间插入、删除和获取随机元素,中等
用 list 存元素的值,用 dic 存元素在 list 中的索引。
添加:加list 末尾,O(1)
删除:在dic 中查找 val 的索引 k,将 list中索引 k 的值与list 末尾值为val1的元素交换,将dic 中val1的索引修改为k,删除 list 末尾元素O(1),删除 dic 中索引 val的条目O(1)
随机:随机返回list的一个值,用 random.choice(list)

class RandomizedSet:

    def __init__(self):
        self.store = []
        self.index = {}
        self.size = 0

    def insert(self, val: int) -> bool:
        if val in self.index:
            return False
        self.store.append(val)
        self.index[val] = self.size
        self.size += 1
        return True

    def remove(self, val: int) -> bool:
        if val not in self.index:
            return False
        k = self.index[val]
        self.store[k] = self.store[-1]
        self.index[self.store.pop()] = k
        self.index.pop(val)
        self.size -= 1
        return True

    def getRandom(self) -> int:
        return choice(self.store)

你可能感兴趣的:(LeetCode学习笔记)