算法-数据结构
金无足赤人无完人,在处理实际问题的时候我们可以使用到很多合适的数据结构,但目前还没有一个数据结构可以称的上完美。查询速度快的,插入的速度就会慢;插入速度和查询速度都快得,占用的空间就会多;占用空间少的,可能会牺牲查询或插入的速度。
所以我们要在不同的问题使用合适的数据结构,就像大家在找对象的时候,他/她不一定是最完美的,但一定是适合你的,希望大家都能找到合适的对象~
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 = key
self.val = val
self.next = None
self.prev = None
class LRUCache:
def __init__(self, capacity: int):
self.size = 0
self.capacity = capacity
self.head = DLinkNode()
self.tail = DLinkNode()
# 虚拟头节点,尾节点
self.head.next = self.tail
self.tail.prev = self.head
self.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.val
else:
return -1
def put(self, key: int, value: int) -> None:
if key in self.cache:
node = self.cache[key]
node.val = value
self.remove_node(node)
self.add_to_head(node)
else:
node = DLinkNode(key, value)
self.add_to_head(node)
self.cache[key] = node
self.size += 1
if self.size > self.capacity:
self.del_tail_node()
self.size -= 1
# 删除节点 O(1)
def remove_node(self, node):
node.next.prev = node.prev
node.prev.next = node.next
# 更新头节点
def add_to_head(self, node):
node.next = self.head.next
self.head.next = node
node.prev = self.head
node.next.prev = node
# 删除尾节点
def del_tail_node(self):
node = self.tail.prev
self.tail.prev = node.prev
self.tail.prev.next = self.tail
self.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_: int
def push(self, val: int) -> None:
if not self.stack:
# 差值为0,最小值为val
self.stack.append(0)
self.min_ = val
else:
diff = val - self.min_
self.stack.append(diff)
# 差值小于零,更新最小值
if diff < 0:
self.min_ = val
def pop(self) -> None:
pop_ = self.stack.pop()
# 说明是最小值
if pop_ < 0:
ret = self.min_
self.min_ += -pop_
return ret
else:
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 False
idx = len(self.arr)
self.dic[val] = idx
self.arr.append(val)
return True
def remove(self, val: int) -> bool:
if val not in self.dic:
return False
idx = self.dic[val]
self.arr[idx] = self.arr[-1]
self.dic[self.arr[idx]] = idx
self.arr.pop()
self.dic.pop(val)
return True
def getRandom(self) -> int:
return choice(self.arr)