题146.LRU缓存机制
题意
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
- LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
- int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
- void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶: 你是否可以在 O(1) 时间复杂度内完成这两种操作?
解题
LRU应该使用 哈希表 和 双向链表 实现
对于get操作,首先判断key是否存在
- 存在,那么返回该节点的值,找到该节点在双向链表中的位置,移到链表的头部
- 不存在,返回 -1
对于 put 操作,首先判断key是否存在:
- 如果key不存在,将输入的 key 和 value 创建一个新节点,添加到双向链表的头部,并且添加到哈希表中。判断双向链表长度是否超出要求,如果超出了,删除双向链表尾部的节点。
对于双向链表,在头部和尾部分别定义一个额外的节点,从而在删除和添加时避免考虑前后节点是否为空。
定义双向链表
class DuLNode:
def __init__(self,key==0,value=0):
self.key=key
self.value=value
self.prev=None
self.next=None
LRU部分
class LRUCache:
def __init__(self,capacity):
self.cache=dict()
self.capacity=capacity
#额外的空节点 head 和 tail
self.head=DuLNode()
self.tail=DuLNode()
self.head.next=self.tail
self.tail.prev=self.head
self.size=0
def get(self,key:int):
if key not in self.cache:
return -1
node=self.cache[key]
self.moveToHead(node)
return node.value
def put(self,key:int,value:int):
if key not in self.cache:
node=DuLNode(key,value) #创建新节点
self.cache[key]=node #添加到哈希表
self.addToHead(node)
self.size+=1
if self.size>self.capacity:#如果超出长度
rem=self.removeTail()#删除双向链表尾部元素
self.cache.pop(rem.key)#删除哈希表中对应节点
self.size-=1
else:
node=self.cache[key]
node.value=value
self.moveToHead(node)
def addToHead(self,node):
node.prev=self.head
node.next=self.head.next
self.head.next.prev=node
self.head.next=node
def moveToHead(self,node):
self.removeNode(node)
self.addToHead(node)
def removeNode(self,node):
node.next.prev=node.prev
node.prev.next=node.next
def removeTail(self):
node=self.tail.prev
self.removeNode(node)
return node
题148.排序链表
题意
给定链表的头节点 head,将链表按照升序排列后返回。
进阶
- 你可以在
O(n log n)
时间复杂度和常数级空间复杂度下,对链表进行排序吗?
解题
作为一道排序题,当看到进阶的时间复杂度要求时,我们就能想到那几种排序方法,首先可以用快速排序来解题,即每次将数组分成两段,分别排序,而后合并。
快速排序
对于本题,可以把每一段链表的 head 的节点值作为基准数,把链表分成 小于、等于、大于 基准数的三段,而后合并。
def sortList(self,head:ListNode)->ListNode:
if not head:
return head
#分成左中右三段
l,m,r=None,None,None
cur=head
while cur:
tmp,cur=cur,cur.next
if tmp.val<head.val:
tmp.next=l
l=tmp
elif tmp.val>head.val:
tmp.next=r
r=tmp
else:
tmp.next=m
m=tmp
l=self.sortList(l)
r=self.sortList(r)
#左中右合并
cur=res=ListNode(None)
for c in [l,m,r]:
while c:
cur.next=c
cur=cur.next
c=c.next
return res.next
时间复杂度达到了进阶的要求,但提交后还是超时了。
尝试第二种方法:归并排序
归并排序
归并排序的思想和快排类似,也是采用了分治的方法,从而达到进阶的时间复杂度要求,具体的做法:
- 用快慢指针找到链表的中间节点,并且从当中切断,逐层切割
- 将左右两段合并为一个升序链表,然后逐层归并,最终合一
def sortList(self,head:ListNode)->ListNode:
if not head or not head.next:
return head
slow,fast=head,head.next
#从当中切断
while fast and fast.next:
slow,fast=slow.next,fast.next.next
mid,slow.next=slow.next,None
left=self.sortList(head)
right=self.sortList(mid)
cur=res=ListNode(None)
while left and right:
if left.val<right.val:
cur,left=left,left.next
else:
cur,right=right,right.next
cur=cur.next
cur.next = left if left else right
return res.next
题155.最小栈
题意
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
- push(x) —— 将元素 x 推入栈中。
- pop() —— 删除栈顶的元素。
- top() —— 获取栈顶元素。
- getMin() —— 检索栈中的最小元素。
解题
参考了官方题解,解题的方式是用一个辅助栈,这个栈有以下特点:
- 和原栈同步进行入栈,出栈操作
- 长度和原栈一样
- 栈顶元素为原栈的最小值
- 入栈,原栈直接入栈,辅助栈对本栈顶的元素和入栈值进行比较,将更小值入栈,这样可以保证:
- 栈顶元素为原栈的最小值
-辅助栈长度和原栈一样
- 栈顶元素为原栈的最小值
def push(self, x: int) -> None:
self.stack.append(x)
self.min_stack.append(min(x,self.min_stack[-1]))
- 出栈,假如原栈出的1是最小值,辅助栈出的则是同样的值,保证辅助栈的栈顶是新的最小值
def pop(self) -> None:
self.stack.pop()
self.min_stack.pop()
- 返回栈顶和最小值
def top(self) -> int:
return self.stack[-1]
def getMin(self) -> int:
return self.min_stack[-1]
另外一种思路,我觉得比较精彩,思路其实和上面一样,但不使用额外的栈 ,改为把原栈的元素改为二元元组。第一个值是原值,第二个值为当前栈内最小值。
- 入栈
def push(self,x):
if not self.stack:
self.stack.append((x,x))
else:
self.stack.append(x,min(self.stack[-1][1],x))
-
出栈直接pop
-
返回最小值:
def getMin(self):
return self.stack[-1][1]
- 返回栈顶
def top(self):
return self.stack[-1][0]