一、203 移除链表元素
问题描述
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
解题思路
双指针
a、设置虚拟节点dmy并进行初始化使其next指向head;
b、初始化双指针cur=head,pre=dmy;
c、判断cur.val是否等于目标值val,相等则令pre.next=cur.next,删除当前节点;
d、否则pre指针向后移动;
e、直到cur移动到链表末尾,最后返回dmy.next。
var removeElements = function(head, val) {
if(head==null) return null;
let dmy=new ListNode(0,head);
let cur=head;
let pre=dmy;
while(cur){
if(cur.val==val){
pre.next=cur.next;
}else{
pre=pre.next;
}
cur=cur.next;
}
return dmy.next;
};
递归
递归调用函数removeElements,传入head.next和 val,如果当前元素值是val,则返回下一个元素,否则直接返回当前元素。
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} val
* @return {ListNode}
*/
var removeElements = function(head, val) {
if(head==null) return null;
head.next=removeElements(head.next,val);
return head.val==val?head.next:head;
};
二、237 删除链表中的节点
有一个单链表的 head,我们想删除它其中的一个节点 node。
给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head。
链表的所有值都是 唯一的,并且保证给定的节点 node 不是链表中的最后一个节点。
删除给定的节点。注意,删除节点并不是指从内存中删除它。这里的意思是:
- 给定节点的值不应该存在于链表中。
- 链表中的节点数应该减少 1。
- node 前面的所有值顺序相同。
- node 后面的所有值顺序相同。
自定义测试: - 对于输入,你应该提供整个链表 head
- 要给出的节点 node。node 不应该是链表的最后一个节点,而应该是链表中的一个实际节点。
- 我们将构建链表,并将节点传递给你的函数。
- 输出将是调用你函数后的整个链表。
解题思路
狸猫换太子
实话实说,看官解之前以为是什么新套路,看到官解那一刻我傻了。
var deleteNode=function(node){
node.val=node.next.val;
node.next=node.next.next;
}
三、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)的平均时间复杂度运行。
解题思路
1、链表+哈希
题意分析:
get()和put()的时间复杂度需要是O(1),显然不能使用数组(数组删除或者添加元素的时间复杂度是O(N),因为需要挪动其他元素),所以考虑使用链表,这样删除和添加元素的时间复杂度是O(1),但是找到这个需要删除的节点需要O(N),所以还需要一个哈希表来记录节点的位置。
下面给出伪代码:
function put(key,value){
if(key存在){
更新key的value;
将该key节点移到链表头部; //方便快速找到最近最久未使用的节点
}else{
if(缓存满了){
删除最后一个节点;
删除其在哈希表中的映射;
}
新建一个节点;
在哈希表中增加映射;
把该节点加入链表头部;
}
}
function get(key){
if(key存在){
返回节点值;
将该节点移动到链表头部;
}else{
返回-1;
}
}
完整代码:
class DoubleLinkedListNode {
constructor(key, value) {
this.key = key
this.value = value
this.prev = null
this.next = null
}
}
class LRUCache {
constructor(capacity) {
this.capacity = capacity
this.usedSpace = 0
// Mappings of key->node.
this.hashmap = {}
this.dummyHead = new DoubleLinkedListNode(null, null)
this.dummyTail = new DoubleLinkedListNode(null, null)
this.dummyHead.next = this.dummyTail
this.dummyTail.prev = this.dummyHead
}
_isFull() {
return this.usedSpace === this.capacity
}
_removeNode(node) {
node.prev.next = node.next
node.next.prev = node.prev
node.prev = null
node.next = null
return node
}
_addToHead(node) {
const head = this.dummyHead.next
node.next = head
head.prev = node
node.prev = this.dummyHead
this.dummyHead.next = node
}
get(key) {
if (key in this.hashmap) {
const node = this.hashmap[key]
this._addToHead(this._removeNode(node))
return node.value
}
else {
return -1
}
}
put(key, value) {
if (key in this.hashmap) {
// If key exists, update the corresponding node and move it to the head.
const node = this.hashmap[key]
node.value = value
this._addToHead(this._removeNode(node))
}
else {
// If it's a new key.
if (this._isFull()) {
// If the cache is full, remove the tail node.
const node = this.dummyTail.prev
delete this.hashmap[node.key]
this._removeNode(node)
this.usedSpace--
}
// Create a new node and add it to the head.
const node = new DoubleLinkedListNode(key, value)
this.hashmap[key] = node
this._addToHead(node)
this.usedSpace++
}
}
}
2、map
a. LRU 缓存策略举例:
i. 假设缓存大小为 4,依次打开了 gitlab、力扣、微信、QQ,缓存链表为 QQ -> 微信 -> 力扣 -> gitlab;
ii. 若此时切换到了「微信」,则缓存链表更新为 微信 -> QQ -> 力扣 -> gitlab;
iii. 若此时打开了腾讯会议,因为缓存已经满 4 个 ,所以要进行缓存淘汰机制,删除链表的最后一位 「gitlab」,则缓存链表更新为 腾讯会议 -> 微信 -> QQ -> 力扣;
b. 本题用的是 map 迭代器,map 实现了 iterator,next 模拟链表的下一个指针,为了方便操作,这里将 map 第一个元素作为链表的最后一个元素;
let cache = new Map();
cache.set('a', 1);
cache.set('b', 2);
cache.set('c', 3);
cache.keys(); // MapIterator {'a', 'b', 'c'}
cache.keys().next().value; // a
// Vue3的keeplive组件就是使用了这种LRU管理组件的缓存
var LRUCache=function(capacity){
this.map=new Map();
this.capacity=capacity;
}
LRUCache.prototype.get=function(key){
if(this.map.has(hey)){
let value=this.map.get(key);
//重新set,相当于更新到map最后
this.map.delete(key);
this.map.set(key,value);
return value;
}
return -1;
}
LRUCache.prototype.put=function(key.value){
//存在,就删除再赋值
if(this.map.has(key)){
this.map.delete(key);
}
this.map.set(key,value);
//判断是否超出容量,进入淘汰机制
if(this.map.size>this.capacity){
this.map.delete(this.map.keys().next().value);
}