提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
今天补两天
请你设计并实现一个满足 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) 的平均时间复杂度运行。
输入
[“LRUCache”, “put”, “put”, “get”, “put”, “get”, “put”, “get”, “get”, “get”]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
1 <= capacity <= 3000
0 <= key <= 10000
0 <= value <= 105
最多调用 2 * 105 次 get 和 put
①首先明确题目是要实现一个能维护被使用队列的哈希表,且存取的时间复杂度为O(1).
那么哈希表的键值如何确定?线性表又该如何实现?
为了对线性表实现O(1)的插入删除,选择用链表实现。
为了根据键精准定位结点在哈希表中的位置,哈希表的元素为【键:结点】。
为了方便根据某结点定位其前后结点,以便随机地插入删除,选择用双向链表。
②双向链表中,规定靠近头部的键值对是最近使用的,靠近尾部的键值对是最久未使用的。对于给定的存在的键,首先使用哈希表进行定位,找出结点在双向链表中的位置,随后进行对双链表的删除、插入操作即可,这些操作都可以在O(1)的时间复杂度内完成。
③需要注意:在双向链表的实现中,使用一个伪头部和伪尾部标记界限,这样在添加节点和删除节点的时候就不需要检查相邻的节点是否存在。此外,双链表结点中必须记载key,因为当容量超限删除尾结点时,要根据key在哈希表中删除该结点
类里调用成员变量、成员函数记得用self.xxx,成员函数直接调用函数名即可
class LinkNode: #双向链表的结点
def __init__(self,key:int,val:int,prev=None,next=None):
#键值
self.key=key
self.val=val
#指针
self.prev=prev
self.next=next
class LRUCache:
def __init__(self, capacity: int):
self.hash=dict() #哈希表
self.head=LinkNode(-1,-1) #伪头结点
self.rear=LinkNode(-1,-1) #伪尾结点
self.head.next=self.rear
self.rear.prev=self.head
self.capacity=capacity #最大容量
self.cur_size=0 #当前大小
def get(self, key: int) -> int:
if(key in self.hash):
node=self.hash[key]
self.node_out(node)#取出node
self.node_in(node)#插入头部
return node.val#返回值
else:
return -1 #找不到key返回-1
def put(self, key: int, value: int) -> None:
if(key in self.hash):
node=self.hash[key]
self.node_out(node) #取出node
node.val=value #改变数值
self.node_in(node) #插入头部
else:
node=LinkNode(key,value) #创建新结点
self.hash[key]=node #哈系表中加入结点
self.node_in(node) #插入
self.cur_size+=1 #变大
if(self.cur_size>self.capacity): #超过容量
node=self.rear.prev
#print(node,'1')
self.hash.pop(node.key)
self.node_out(node) #队尾结点删除
#取出结点
def node_out(self,node):
#print(node)
node.next.prev=node.prev
node.prev.next=node.next
#头插结点
def node_in(self,node):
node.next=self.head.next
self.head.next=node
node.prev=self.head
node.next.prev=node
给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。
插入排序 算法的步骤:
插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。
下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。
对链表进行插入排序。
示例 1:
输入: head = [4,2,1,3]
输出: [1,2,3,4]
示例 2:
输入: head = [-1,5,3,4,0]
输出: [-1,0,3,4,5]
列表中的节点数在 [1, 5000]范围内
-5000 <= Node.val <= 5000
遍历链表,对于每个元素执行插入,时间复杂度O(n^2)
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def insertionSortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
cur=head #指向当前插入元素的指针
Head=ListNode(-5001,None) #伪头结点,便于在最前面插入
while(cur != None):
temp=cur.next #先记住cur的下一个结点
p=Head #先指向头结点
while(1): #遍历前面的有序序列
#print(p.val,p.next)
if(p.val<=cur.val and (p.next==None or p.next.val>cur.val)): #找到合适位置插入节点
temp2=p.next
p.next=cur
cur.next=temp2
break
p=p.next #继续循环
cur=temp #下一个插入结点
return Head.next