算法-数据结构
金无足赤人无完人,在处理实际问题的时候我们可以使用到很多合适的数据结构,但目前还没有一个数据结构可以称的上完美。查询速度快的,插入的速度就会慢;插入速度和查询速度都快得,占用的空间就会多;占用空间少的,可能会牺牲查询或插入的速度。
所以我们要在不同的问题使用合适的数据结构,就像大家在找对象的时候,他/她不一定是最完美的,但一定是适合你的,希望大家都能找到合适的对象~
146. LRU 缓存
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache
类:
LRUCache(int capacity)
以 正整数 作为容量capacity
初始化 LRU 缓存int get(int key)
如果关键字key
存在于缓存中,则返回关键字的值,否则返回-1
。void put(int key, int value)
如果关键字key
已经存在,则变更其数据值value
;如果不存在,则向缓存中插入该组key-value
。如果插入操作导致关键字数量超过capacity
,则应该 逐出 最久未使用的关键字。
分析
- 函数
get
和put
必须以O(1)
的平均时间复杂度运行。 - 能支持
get
O(1)
的有哈希表、数组,支持put
O(1)
的有可变数组的末尾元素、哈希表、双向链表 - 因为
put
的对象需要是最不长使用的数据,没有要确保最不长使用的数据快速被找的,所以使用的是链表,链表可以快速更新节点位置
class DLinkNode():def __init__(self, key=0, val=0):self.key = keyself.val = valself.next = Noneself.prev = Noneclass LRUCache:def __init__(self, capacity: int):self.size = 0self.capacity = capacityself.head = DLinkNode()self.tail = DLinkNode()# 虚拟头节点,尾节点self.head.next = self.tailself.tail.prev = self.headself.cache = dict()def get(self, key: int) -> int:if key in self.cache:node = self.cache[key]# 更新头节点self.remove_node(node)self.add_to_head(node)return node.valelse:return -1def put(self, key: int, value: int) -> None:if key in self.cache:node = self.cache[key]node.val = valueself.remove_node(node)self.add_to_head(node)else:node = DLinkNode(key, value)self.add_to_head(node)self.cache[key] = nodeself.size += 1if self.size > self.capacity:self.del_tail_node()self.size -= 1# 删除节点 O(1)def remove_node(self, node):node.next.prev = node.prevnode.prev.next = node.next# 更新头节点def add_to_head(self, node):node.next = self.head.nextself.head.next = nodenode.prev = self.headnode.next.prev = node# 删除尾节点def del_tail_node(self):node = self.tail.prevself.tail.prev = node.prevself.tail.prev.next = self.tailself.cache.pop(node.key)# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
155. 最小栈
设计一个支持 push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack
类:
MinStack()
初始化堆栈对象。void push(int val)
将元素val推入堆栈。void pop()
删除堆栈顶部的元素。int top()
获取堆栈顶部的元素。int getMin()
获取堆栈中的最小元素。
分析
push
pop
top
可通过变长数组完成getMin()
是关键, 我们可以通过另一个变长数组记录当前的最小值- 记录数组a=[4,5,6,2,3,1]
- 最小值数组b=[4,4,4,2,2,1]
- 差值记录a=[0,1,2,-2,1,-1]:前一个最小值可以通过当前最小值减去记录数组的值:1-(-1)= 2;2-(-2)= 4。具体可看代码
class MinStack:def __init__(self):# 记录差值self.stack = []# 记录最小值self.min_: intdef push(self, val: int) -> None:if not self.stack:# 差值为0,最小值为valself.stack.append(0)self.min_ = valelse:diff = val - self.min_self.stack.append(diff)# 差值小于零,更新最小值if diff < 0:self.min_ = valdef pop(self) -> None:pop_ = self.stack.pop()# 说明是最小值if pop_ < 0:ret = self.min_self.min_ += -pop_return retelse:return self.min_ + pop_def top(self) -> int:if self.stack[-1] <= 0:return self.min_else:return self.min_ + self.stack[-1]def getMin(self) -> int:return self.min_
380. O(1) 时间插入、删除和获取随机元素
实现RandomizedSet
类:
RandomizedSet()
初始化RandomizedSet
对象bool insert(int val)
当元素val
不存在时,向集合中插入该项,并返回true
;否则,返回false
。bool remove(int val)
当元素val
存在时,从集合中移除该项,并返回true
;否则,返回false
。int getRandom()
随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。
你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1)
。
insert
remove
可通过哈希表,变成数组末尾、链表末尾实现getRandom()
只能通过数组实现,因为数组可以直接取数且任意一个元素出现概率是相等的
用哈希表记录元素下标,删除时将删除元素与末尾元素交换,这样就相对于删除末尾元素了
class RandomizedSet:def __init__(self):self.arr = []self.dic = dict()def insert(self, val: int) -> bool:if val in self.dic:return Falseidx = len(self.arr)self.dic[val] = idxself.arr.append(val)return Truedef remove(self, val: int) -> bool:if val not in self.dic:return Falseidx = self.dic[val]self.arr[idx] = self.arr[-1]self.dic[self.arr[idx]] = idxself.arr.pop()self.dic.pop(val)return Truedef getRandom(self) -> int:return choice(self.arr)
2671. 频率跟踪器
211. 添加与搜索单词 - 数据结构设计