文章目录
哈希表
概念
- 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、重哈希:
- 上述两种方法不适合元素一直插入的情况,元素一直插入会导致底层数组不够用,使用重哈希方式,当数组使用到了一定比例(负载因子),会触发重哈希算法,重哈希会重新建立一个更大的数组,比如扩大两倍,然后把之前的数组元素重新放入到新的数组里
- 1、链接法:
- 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)
-