Python数据结构和算法笔记七:哈希表

哈希表

概念
  • 1、哈希表是一种快速查找结构
  • 2、经常用来存储“键值对”,key/value值
  • 3、哈希表的查找时间近似为O(1),几乎可以瞬间查找到一个值
  • 4、Java HashMap,Python的dict/set底层就是使用哈希表实现的,我们可以快速查找一个key的值
疑问
  • 1、如果传入的值通过散列函数计算的下标一样怎么办?如何解决哈希冲突
    • 1、链接法:
      • 可以在当前位置挂一个链表,将冲突的数据都链接起来。
      • 缺点:极端情况下一个链表可能会比较长
    • 2、开放寻址法(Open Addressing):
      • 线性探查(Linear Probing:f(i) = i)
      • 二次方探查(Quadratic Probing:f(i) = i*i)
      • 双重散列(Double Hashing:f(i) = i*hash2(elem))
    • 3、重哈希:
      • 上述两种方法不适合元素一直插入的情况,元素一直插入会导致底层数组不够用,使用重哈希方式,当数组使用到了一定比例(负载因子),会触发重哈希算法,重哈希会重新建立一个更大的数组,比如扩大两倍,然后把之前的数组元素重新放入到新的数组里
  • 2、哈希表底层一般使用一个数组来实现。思考如何存一个k/v结构
  • 3、通过对散列函数计算key的下标插入到对应位置
哈希表的应用
  • 1、Python dict/set 底层就是使用哈希表实现
  • 2、dict可以快速保存键值对
  • 3、set常用来判断元素是否存在,set也可以使用dict来实现
哈希表的练习
  • 掌握leetcode 哈希表和集合经典例题
  • 掌握LRU cache (最近最少使用缓存算法)
两个数组的交集
  • 思路:利用set的特性,熟悉set里面的方法使用

    • 给定两个数组,编写一个函数来计算它们的交集。
      
      示例 1:
      输入:nums1 = [1,2,2,1], nums2 = [2,2]
      输出:[2]
      
      示例 2:
      输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
      输出:[9,4]
      
    • class Solution(object):
          def intersection(self, nums1, nums2):
              """
              :type nums1: List[int]
              :type nums2: List[int]
              :rtype: List[int]
              """
              return set(nums1) & set(nums2)
      
两个数组的交集 II
  • 思路:

    • 1、哈希表

    • 2、指针排序(下面未实现)

    • 给定两个数组,编写一个函数来计算它们的交集。
      
      示例 1:
      输入:nums1 = [1,2,2,1], nums2 = [2,2]
      输出:[2,2]
      
      示例 2:
      输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
      输出:[4,9]
      
    • import collections
      
      # 哈希表,时间复杂度O(max(n,m)),空间复杂度O(max(n,m))
      class Solution:
          def intersect(self, nums1, nums2):
              if len(nums1) > len(nums2):
                  return self.intersect(nums2, nums1)
      
              m = collections.Counter()
              for num in nums1:
                  m[num] += 1
      
              intersection = list()
              for num in nums2:
                  if (m.get(num, 0)) > 0:
                      intersection.append(num)
                      m[num] -= 1
                      if m[num] == 0:
                          m.pop(num)
      
              return intersection
      
LRU缓存机制
  • 解决缓存空间不够用的一种常见的高效淘汰策略

  • 思路:

    • 1、题目说O(1)时间复杂度,说明空间复杂度不限,限定了容量。

    • 2、使用字典存储key/value值,查询只需要根据关键字查找所以是O(1)

    • 3、记录key对应的顺序,且要对key进行移位操作,使用**(循环)双向链表**,双向链表移位插入的时间复杂度为O(1)

    • 4、Python内置的collections模块中的OrderedDict类底层是字典+循环双向链表,正好适合此题

    • 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
      
      获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。
      写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
      
       
      你是否可以在 O(1) 时间复杂度内完成这两种操作?示例:
      
      LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
      
      cache.put(1, 1);
      cache.put(2, 2);
      cache.get(1);       // 返回  1
      cache.put(3, 3);    // 该操作会使得关键字 2 作废
      cache.get(2);       // 返回 -1 (未找到)
      cache.put(4, 4);    // 该操作会使得关键字 1 作废
      cache.get(1);       // 返回 -1 (未找到)
      cache.get(3);       // 返回  3
      cache.get(4);       // 返回  4
      
    • from collections import OrderedDict
      
      
      class LRUCache(object):
      
          def __init__(self, capacity:int):
              """
              :type capacity: int
              """
              self.od = OrderedDict()
              self.capacity = capacity
      
          def get(self, key):
              """
              :type key: int
              :rtype: int
              """
              res = self.od.get(key)
              if res:
                  self.od.move_to_end(key)
                  return res
              else:
                  return -1
      
          def put(self, key, value):
              """
              :type key: int
              :type value: int
              :rtype: None
              """
              self.od[key] = value
              self.od.move_to_end(key)
              if len(self.od) > self.capacity:
                  self.od.popitem(last=False)
      
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值