1、leetcode146 LRU缓存机制
思路:
- 需要一个双向链表维护所有当前存在的节点,被新插入\修改\查询的节点要被移动到链表头。如果在节点内部维护一个时间戳,则每次操作都需要更新所有节点,不满足O1时间复杂度。
- 插入新节点后size加一,如果此时size超过了capacity,则需要淘汰链表尾的节点以维持capacity。
- 由于要求O1的读写,需要一个哈希表,以节点key值为key,以节点指针为value。
struct node{
node* prev;
node* next;
int key;
int value;
node(): key(0), value(0), prev(nullptr), next(nullptr) {}
node(int k, int v): key(k), value(v), prev(nullptr), next(nullptr) {}
};
class LRUCache {
public:
LRUCache(int capacity): size(0), cap(capacity) {
head = new node();
tail = new node();
head->next = tail;
tail->prev = head;
}
int get(int key) {
if(!map.count(key)) return -1;
node* p = map[key];
removeNode(p);
addToHead(p);
return p->value;
}
void put(int key, int value) {
if(!map.count(key)){
node* p = new node(key, value);
addToHead(p);
map.emplace(key, p);
++size;
if(size > cap){
node* q = tail->prev;
removeNode(q);
map.erase(q->key);
delete q;
}
}
else{
node* p = map[key];
p->value = value;
removeNode(p);
addToHead(p);
}
}
void removeNode(node* p){
p->next->prev = p->prev;
p->prev->next = p->next;
}
void addToHead(node* p){
p->next = head->next;
p->prev = head;
head->next->prev = p;
head->next = p;
}
private:
unordered_map<int, node*> map;
int size;
int cap;
node* head;
node* tail;
};
2、leetcode3 无重复字符的最长子串
思路:
- 注意是子串,不是子序列,因此非常适用于滑窗
- 外层循环每次将右指针加一,内层循环检查当前窗口有没有右指针指向的字符,有则一直将左指针加一。
- 每次外层循环更新答案最大值(内存循环不需要,因为内层循环必然是长度减小)
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_set<char> window;
int ans = 0;
for(int l = 0, r = 0; r < s.size(); ++r){
while(l <= r && window.count(s[r])){
window.erase(s[l]);
++l;
}
window.emplace(s[r]);
ans = max(ans, r - l + 1);
}
return ans;
}
};
今天先写这两道,说实话滑窗的常用模板都快忘了,调了半天。 不每天练真不行吧。