leetcode 146. LRU 缓存机制

191 阅读1分钟

力扣题目1
力扣题目2,和题目1相同
牛客题目

本题是数据结构设计题。
为实现在O(1)O(1)时间复杂度内完成put和get两种操作,LRUCache基于一个哈希表(map)和一个双向链表来实现。哈希表的插入、查询和删除的时间复杂度都是O(1)O(1),双向链表的插入、删除操作的时间复杂度都是O(1)O(1),它们共同作用使得get和put操作的时间复杂度是O(1)O(1)。请看代码细节。
具体解答如下。牛客网题目的解法在此基础上稍作修改即可。

基于自定义的双向链表(已提交通过)

type LRUCache struct {
	m map[int]*Item
	list *DoublyLinkedList
}


func Constructor(capacity int) LRUCache {
	if capacity < 1 {
		panic("capacity < 1")
	}
	return LRUCache{
		m: make(map[int]*Item, capacity),
		list: NewDoublyLinkedList(capacity),
	}
}


func (this *LRUCache) Get(key int) int {
	item, ok := this.m[key]
	if !ok {
		return -1
	}
	this.list.Move2Head(item) // 设置为最“热”数据
	return item.Val
}


func (this *LRUCache) Put(key int, value int)  {
	item, ok := this.m[key]
	if ok { // 已存在
		item.Val = value // 更新value
		this.list.Move2Head(item) // 设置为“最热”数据
	} else {
                // 插入新数据
		item, removed := this.list.AddToHead(key, value)
                this.m[key] = item
		if removed != nil {
                        // 有链表节点被删除了,要同步删除map中的数据
			delete(this.m, removed.Key)
		}
	}
}


/**
 * Your LRUCache object will be instantiated and called as such:
 * obj := Constructor(capacity);
 * param_1 := obj.Get(key);
 * obj.Put(key,value);
 */

// 双向链表节点
type Item struct {
	Key int // 关键字
	Val int // 值
	Prev *Item // 指向直接前驱
	Next *Item // 指向直接后继
}

// 双向链表
type DoublyLinkedList struct {
	head *Item // 头节点
	tail *Item // 尾节点
	size int // 当前节点个数
	capacity int // 最大节点个数限制
}

func NewDoublyLinkedList(capacity int) *DoublyLinkedList {
	return &DoublyLinkedList{
		capacity: capacity,
	}
}

func (o *DoublyLinkedList) AddToHead(key int, val int) (item, removed *Item) {
	item = &Item{
		Key: key,
		Val: val,
	}
	if o.head != nil {
		item.Next = o.head
		o.head.Prev = item
		o.head = item
	} else {
		o.head = item
		o.tail = item
	}
	o.size++
	if o.size > o.capacity {
		removed = o.removeTail()
	}
	return
}

func (o *DoublyLinkedList) removeTail() *Item {
        // 此处o.head和o.tail肯定不是nil
	tail := o.tail
	if o.head != o.tail {
		prev := o.tail.Prev // 此处prev肯定不是nil
		prev.Next = nil // 注意置nil
		o.tail = prev
	} else {
		o.head = nil
		o.tail = nil
	}
	o.size--
	return tail
}

func (o *DoublyLinkedList) Move2Head(item *Item) {
	if item == o.head {
		return
	}

        // 此处item.Prev肯定不是nil
	item.Prev.Next = item.Next
	if item != o.tail {
		item.Next.Prev = item.Prev // 此处item.Next肯定不是nil
	} else {
		o.tail = item.Prev
	}

	item.Prev = nil // 注意置nil
	item.Next = o.head
	o.head.Prev = item
	o.head = item
}

基于container/list(已提交通过)

import "container/list"

type LRUCache struct {
	m map[int]*list.Element
	list *list.List
        capacity int
}


func Constructor(capacity int) LRUCache {
	if capacity < 1 {
		panic("capacity < 1")
	}
	return LRUCache{
		m: make(map[int]*list.Element, capacity),
		list: list.New(),
                capacity: capacity,
	}
}


func (this *LRUCache) Get(key int) int {
	e, ok := this.m[key]
	if !ok {
		return -1
	}
	this.list.MoveToFront(e) // 设置为最“热”数据
	return e.Value.(*Pair).Value
}


func (this *LRUCache) Put(key int, value int)  {
	e, ok := this.m[key]
	if ok { // 已存在
		e.Value.(*Pair).Value = value // 更新value
		this.list.MoveToFront(e) // 设置为“最热”数据
	} else {
                if this.list.Len() == this.capacity {
                        e = this.list.Back()
                        delete(this.m, e.Value.(*Pair).Key)
                        this.list.Remove(e)
                }
                // 插入新数据
                this.m[key] = this.list.PushFront(&Pair{
                        Key: key,
                        Value: value,
                })
	}
}

type Pair struct {
        Key int
        Value int
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * obj := Constructor(capacity);
 * param_1 := obj.Get(key);
 * obj.Put(key,value);
 */