哈希表

259 阅读4分钟
  • 设计哈希表

    • 705. 设计哈希集合

      class MyHashSet:
          def __init__(self):
              self.keyRange = 769
              self.bucketArray = [Bucket() for i in range(self.keyRange)]
      
          def _hash(self, key):
              return key % self.keyRange
      
          def add(self, key):
              bucketIndex = self._hash(key)
              self.bucketArray[bucketIndex].insert(key)
      
          def remove(self, key):
              bucketIndex = self._hash(key)
              self.bucketArray[bucketIndex].delete(key)
      
          def contains(self, key):
              bucketIndex = self._hash(key)
              return self.bucketArray[bucketIndex].exists(key)
      
      
      class Bucket:
          def __init__(self):
              self.tree = BSTree()
      
          def insert(self, val):
              self.tree.root = self.tree.insertIntoBST(self.tree.root, val)
      
          def delete(self, val):
              self.tree.root = self.tree.deleteNode(self.tree.root, val)
      
          def exists(self, val):
              return bool(self.tree.searchBST(self.tree.root, val))
      
      
      class TreeNode:
          def __init__(self, value, left=None, right=None):
              self.val = value
              self.left = left
              self.right = right
      
      class BSTree:
          def __init__(self):
              self.root = None
      
          def searchBST(self, root, val):
              if root is None or val == root.val: return root
              return self.searchBST(root.left, val) if val < root.val else self.searchBST(root.right, val)
      
          def insertIntoBST(self, root, val):
              if not root: return TreeNode(val)
              if val > root.val:
                  root.right = self.insertIntoBST(root.right, val)
              elif val == root.val:
                  return root
              else:
                  root.left = self.insertIntoBST(root.left, val)
              return root
      
          def successor(self, root):
              root = root.right
              while root.left:
                  root = root.left
              return root.val
      
          def predecessor(self, root):
              root = root.left
              while root.right:
                  root = root.right
              return root.val
      
          def deleteNode(self, root, key):
              if not root: return None
              if key > root.val:
                  root.right = self.deleteNode(root.right, key)
              elif key < root.val:
                  root.left = self.deleteNode(root.left, key)
              else:
                  if not (root.left or root.right):
                      root = None
                  elif root.right:
                      root.val = self.successor(root)
                      root.right = self.deleteNode(root.right, root.val)
                  else:
                      root.val = self.predecessor(root)
                      root.left = self.deleteNode(root.left, root.val)
              return root
      
      
      # Your MyHashSet object will be instantiated and called as such:
      # obj = MyHashSet()
      # obj.add(key)
      # obj.remove(key)
      # param_3 = obj.contains(key)
      
    • 706. 设计哈希映射

      class Bucket:
          def __init__(self):
              self.bucket = []
      
          def get(self, key):
              for k, v in self.bucket:
                  if k == key:
                      return v
              return -1
      
          def update(self, key, val):
              found = False
              for i, (k, v) in enumerate(self.bucket):
                  if key == k:
                      self.bucket[i] = (key, val)
                      found = True
                      break
              if not found:
                  self.bucket.append((key, val))
      
          def remove(self, key):
              for i, (k, v) in enumerate(self.bucket):
                  if key == k:
                      del self.bucket[i]
      
      
      class MyHashMap:
          def __init__(self):
              self.key_space = 769
              self.hash_table = [Bucket() for _ in range(self.key_space)]
      
          def put(self, key, val):
              idx = key % self.key_space
              self.hash_table[idx].update(key, val)
      
          def get(self, key):
              idx = key % self.key_space
              return self.hash_table[idx].get(key)
      
          def remove(self, key):
              idx = key % self.key_space
              self.hash_table[idx].remove(key)
      
      
      # Your MyHashMap object will be instantiated and called as such:
      # obj = MyHashMap()
      # obj.put(key,value)
      # param_2 = obj.get(key)
      # obj.remove(key)
      
  • 哈希表与滑动窗口

    • 219. 存在重复元素 II

      class Solution:
          def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
              window = Counter()
              left = 0
      
              for right, ch in enumerate(nums):
                  window[ch] += 1
                  while left <= right and right-left+1 > k+1:
                      window[nums[left]] -= 1
                      left += 1
                  if window[ch] > 1:
                      return True
      
              return False
      
    • 424. 替换后的最长重复字符

      class Solution:
          def characterReplacement(self, s: str, k: int) -> int:
              window = Counter()
              ans = maxn = left = 0
      
              for right, ch in enumerate(s):
                  window[ch] += 1
                  maxn = max(maxn, window[ch])
                  # 滑动窗口大小 - 窗口里出现次数最多的字母次数 = 其他字母的总个数
                  while left <= right and right-left+1 - maxn > k:
                      window[s[left]] -= 1
                      left += 1
                  ans = max(ans, right-left+1)
      
              return ans
      
    • 1004. 最大连续1的个数 III

      class Solution:
          def longestOnes(self, nums: List[int], k: int) -> int:
              window = Counter()
              ans = maxn = left = 0
      
              for right, num in enumerate(nums):
                  window[num] += 1
                  maxn = max(maxn, window[1])
                  while left <= right and right-left+1 - maxn > k:
                      window[nums[left]] -= 1
                      left += 1
                  ans = max(ans, right-left+1)
      
              return ans
      
    • 3. 无重复字符的最长子串

      class Solution:
          def lengthOfLongestSubstring(self, s: str) -> int:
              window = Counter()
              ans = left = 0
      
              for right, ch in enumerate(s):
                  window[ch] += 1
                  while left <= right and right-left+1 != len(window):
                      window[s[left]] -= 1
                      if window[s[left]] == 0:
                          del window[s[left]]
                      left += 1
                  ans = max(ans, right-left+1)
      
              return ans
      
    • 159. 至多包含两个不同字符的最长子串

      class Solution:
          def lengthOfLongestSubstringTwoDistinct(self, s: str) -> int:
              window = Counter()
              ans = left = 0
      
              for right, ch in enumerate(s):
                  window[ch] += 1
                  while left <= right and len(window) > 2:
                      window[s[left]] -= 1
                      if window[s[left]] == 0:
                          del window[s[left]]
                      left += 1
                  ans = max(ans, right-left+1)
      
              return ans
      
  • 实际应用 - 设计键

    • 49. 字母异位词分组

      class Solution:
          def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
              # 字符串中对应字符个数一致的时候
              def hashKey(s: str) -> Tuple[int]:
                  count = [0] * 26
                  for ch in s:
                      count[ord(ch) - ord('a')] += 1
                  return tuple(count)
      
              ans = defaultdict(list)
              for s in strs:
                  ans[hashKey(s)].append(s)
              return list(ans.values())
      
    • 249. 移位字符串分组

      class Solution:
          def groupStrings(self, strings: List[str]) -> List[List[str]]:
              # 字符串之间可以移位得到必然是每一位距离差对应一致的时候
              def hashKey(s: str) -> Tuple[int]:
                  n = len(s)
                  if n <= 1:
                      return 0
                  diff = []
                  for i in range(1, n):
                      diff.append((ord(s[i]) - ord(s[i-1])) % 26)
                  return tuple(diff)
      
              ans = defaultdict(list)
              for s in strings:
                  ans[hashKey(s)].append(s)
              return list(ans.values())
      
    • 36. 有效的数独

      class Solution:
          def isValidSudoku(self, board: List[List[str]]) -> bool:
              # 在矩阵中,你可能会使用 行索引 或 列索引 作为键
              # 如果需要将矩阵分块,可以将行索引和列索引进行组合以标识该元素属于哪个块
              # 有时,在矩阵中,你可能会希望将对角线的元素组合在一起,比如八皇后问题
              row = [[0] * 10 for _ in range(9)]
              col = [[0] * 10 for _ in range(9)]
              block = [[0] * 10 for _ in range(9)]
      
              for i in range(9):
                  for j in range(9):
                      if board[i][j] != '.':
                          num = int(board[i][j])
                          blockIdx = (i // 3) * 3 + j // 3
                          if row[i][num] or col[j][num] or block[blockIdx][num]:
                              return False
                          row[i][num] = col[j][num] = block[blockIdx][num] = 1
              return True
      
    • 652. 寻找重复的子树

      # Definition for a binary tree node.
      # class TreeNode:
      #     def __init__(self, val=0, left=None, right=None):
      #         self.val = val
      #         self.left = left
      #         self.right = right
      class Solution:
          def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]:
              ans = defaultdict(list)
              def dfs(node: Optional[TreeNode]) -> str:
                  if not node:
                      return '#'
                  # serial = '{},{},{}'.format(dfs(node.left), dfs(node.right), node.val) 也可以,
                  # 但是不能node.val写中间,因为写中间无法区分等高的斜子树(比如说两棵树等高为2,但是一个左斜,一个右斜)
                  leftRet = dfs(node.left)
                  rightRet = dfs(node.right)
                  def hashKey():
                      return '{},{},{}'.format(node.val, leftRet, rightRet)
      
                  serial = hashKey()
                  ans[serial].append(node)
                  return serial
      
              dfs(root)
              return [ans[k][0] for k in ans.keys() if len(ans[k]) >= 2]
      
    • 311. 稀疏矩阵的乘法

      class Solution:
          def multiply(self, mat1: List[List[int]], mat2: List[List[int]]) -> List[List[int]]:
              r1, c1 = len(mat1), len(mat1[0])
              r2, c2 = len(mat2), len(mat2[0])
              m1 = defaultdict(Counter)
              for r in range(r1):
                  for c in range(c1):
                      if mat1[r][c] != 0:
                          m1[r][c] = mat1[r][c]
      
              m2 = defaultdict(Counter)
              for c in range(c2):
                  for r in range(r2):
                      if mat2[r][c] != 0:
                          m2[c][r] = mat2[r][c]
      
              ans = [[0] * c2 for _ in range(r1)]
              for k1, v1 in m1.items():
                  for k2, v2 in m2.items():
                      ans[k1][k2] = sum([v * v2[k] for k, v in v1.items()])
      
              return ans
      
  • 小结与讨论

    • 454. 四数相加 II

      class Solution:
          def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
              countAB = Counter([u + v for u in nums1 for v in nums2])
              ans = 0
              for u in nums3:
                  for v in nums4:
                      if -u - v in countAB:
                          ans += countAB[-u - v]
              return ans
      
    • 380. O(1) 时间插入、删除和获取随机元素

      class RandomizedSet:
      
          def __init__(self):
              self.d = {}
              self.l = []
      
          def insert(self, val: int) -> bool:
              if val in self.d:
                  return False
              self.d[val] = len(self.l)
              self.l.append(val)
              return True
      
          # 需要删除时可以交换val和list[-1],然后删除pop就是O(1)的
          def remove(self, val: int) -> bool:
              if val in self.d:
                  last_elem = self.l[-1]
                  idx = self.d[val]
                  self.d[last_elem] = idx
                  self.l[idx] = last_elem
                  self.l.pop()
                  del self.d[val]
                  return True
              return False
      
          def getRandom(self) -> int:
              return random.choice(self.l)