福哥答案2021-01-23:
这道题复杂度太高,短时间内很难写出来。面试的时候不建议手撕代码。
一个存节点的map+一个存桶的map+一个存桶的双向链表。桶本身也是一个双向链表。
存节点的map:key是键,value是节点。
存桶的map:key是次数,value是桶。
代码用golang编写,代码如下:

package main

import (
"container/list"
"fmt"
) func main() {
cache := Constructor(2)
cache.Put(1, 1)
cache.Put(2, 2)
cache.Get(1) // 返回 1
cache.Put(3, 3) // 去除键 2
cache.Get(2) // 返回 -1(未找到)
cache.Get(3) // 返回 3
cache.Put(4, 4) // 去除键 1
cache.Get(1) // 返回 -1(未找到)
cache.Get(3) // 返回 3
cache.Get(4) // 返回 4 } type LFUCache struct {
Cap int
Len int
//map缓存,键存key,值存kv和前后
KeyCache map[int]*list.Element //key存键,value存节点 List *list.List FreqCache map[int]*list.Element //key存次数,value存桶
} func Constructor(capacity int) LFUCache {
ret := LFUCache{}
ret.Cap = capacity
ret.KeyCache = make(map[int]*list.Element) //元素存节点
ret.FreqCache = make(map[int]*list.Element) //元素存桶
ret.List = list.New()
return ret
} func (this *LFUCache) Get(key int) int {
//已经找到当前元素了
v := this.KeyCache[key]
if v == nil {
fmt.Println(-1)
return -1
}
//移动
this.curNodeMoveToNextBucket(v) //返回当前元素的值
fmt.Println(v.Value.([]int)[1])
return v.Value.([]int)[1]
} //当前节点移动到下一个桶
func (this *LFUCache) curNodeMoveToNextBucket(v *list.Element) {
//根据当前节点的次数找到当前桶
curbucket := this.FreqCache[v.Value.([]int)[2]] //找下一桶,找不到创建新桶
nextbucket := this.FreqCache[v.Value.([]int)[2]+1]
if nextbucket == nil {
nextbucket = this.List.InsertAfter(list.New(), curbucket)
this.FreqCache[v.Value.([]int)[2]+1] = nextbucket
} //把当前节点放在下一桶里
//nextbucket.Value.(*list.List).PushBack(v.Value),这样的代码,leetcode不能通过。原因是元素移动后,已经不是以前的元素了。所以map需要重新赋值。这个错误,我花了1个小时才找到,请谨慎。
this.KeyCache[v.Value.([]int)[0]] = nextbucket.Value.(*list.List).PushBack(v.Value) //当前桶删除当前节点
curbucket.Value.(*list.List).Remove(v) //如果当前桶为空,直接删除当前桶。
if curbucket.Value.(*list.List).Len() == 0 {
this.List.Remove(curbucket)
delete(this.FreqCache, v.Value.([]int)[2])
} //当前节点次数加1
v.Value.([]int)[2]++
} func (this *LFUCache) Put(key int, value int) {
if this.Cap == 0 {
return
}
if v, ok := this.KeyCache[key]; ok { //缓存里有
//修改值
v.Value.([]int)[1] = value //移动
this.curNodeMoveToNextBucket(v)
} else { //缓存里没有
if this.Len == this.Cap {
//获取可能需要删除的桶
deleteBucket := this.List.Front() //获取需要删除的元素
deleteE := deleteBucket.Value.(*list.List).Front() //删除元素
delete(this.KeyCache, deleteE.Value.([]int)[0])
deleteBucket.Value.(*list.List).Remove(deleteE) //可能需要删除的桶如果没有元素,删除桶。并且需要删除的元素的次数不是1
if deleteBucket.Value.(*list.List).Len() == 0 {
this.List.Remove(deleteBucket)
delete(this.FreqCache, deleteE.Value.([]int)[2])
}
} else {
this.Len++
} //获取次数为1的桶
oneTimeBucket := this.FreqCache[1] //获取不到就创建桶
if oneTimeBucket == nil {
oneTimeBucket = this.List.PushFront(list.New())
this.FreqCache[1] = oneTimeBucket
} this.KeyCache[key] = oneTimeBucket.Value.(*list.List).PushBack([]int{key, value, 1}) }
}

  执行结果如下:

2021-01-23:LFU手撸,说下时间复杂度和空间复杂度。的更多相关文章

  1. 手撸一个SpringBoot-Starter

    1. 简介 通过了解SpringBoot的原理后,我们可以手撸一个spring-boot-starter来加深理解. 1.1 什么是starter spring官网解释 starters是一组方便的依 ...

  2. 【分布式锁的演化】终章!手撸ZK分布式锁!

    前言 这应该是分布式锁演化的最后一个章节了,相信很多小伙伴们看完这个章节之后在应对高并发的情况下,如何保证线程安全心里肯定也会有谱了.在实际的项目中也可以参考一下老猫的github上的例子,当然代码没 ...

  3. 手撸一个springsecurity,了解一下security原理

    手撸一个springsecurity,了解一下security原理 转载自:www.javaman.cn 手撸一个springsecurity,了解一下security原理 今天手撸一个简易版本的sp ...

  4. php手撸轻量级开发(一)

    聊聊本文内容 之前讲过php简单的内容,但是原生永远是不够看的,这次用框架做一些功能性的事情. 但是公司用自己的框架不能拿出来,用了用一些流行的框架比如tp,larveral之类的感觉太重,CI也不顺 ...

  5. 使用Java Socket手撸一个http服务器

    原文连接:使用Java Socket手撸一个http服务器 作为一个java后端,提供http服务可以说是基本技能之一了,但是你真的了解http协议么?你知道知道如何手撸一个http服务器么?tomc ...

  6. 康少带你手撸orm

    orm 什么是orm? 对象关系映射: 一个类映射成一张数据库的表 类的对象映射成数据库中的一条条数据 对象点数据映射成数据库某条记录的某个值 优点:不会写sql语句的程序员也可以很6的操作sql语句 ...

  7. .NET手撸2048小游戏

    .NET手撸2048小游戏 2048是一款益智小游戏,得益于其规则简单,又和2的倍数有关,因此广为人知,特别是广受程序员的喜爱. 本文将再次使用我自制的"准游戏引擎"FlysEng ...

  8. .NET手撸绘制TypeScript类图——下篇

    .NET手撸绘制TypeScript类图--下篇 在上篇的文章中,我们介绍了如何使用.NET解析TypeScript,这篇将介绍如何使用代码将类图渲染出来. 注:以防有人错过了,上篇链接如下:http ...

  9. 纯手撸web框架

    纯手撸web框架 一.Web应用的组成 接下来我们学习的目的是为了开发一个Web应用程序,而Web应用程序是基于B/S架构的,其中B指的是浏览器,负责向S端发送请求信息,而S端会根据接收到的请求信息返 ...

  10. 99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!|建议收藏!!!

    罗曼罗兰说过:世界上只有一种英雄主义,就是看清生活的真相之后依然热爱生活. 对于 Lombok 我相信大部分人都不陌生,但对于它的实现原理以及缺点却鲜为人知,而本文将会从 Lombok 的原理出发,手 ...

随机推荐

  1. ES2016-ES2020

    参考:https://zhuanlan.zhihu.com/p/59096242 备注:可以使用ES6取代的10个Lodash特性 https://www.w3cplus.com/javascript ...

  2. Axios的相关应用

    Axios 的案例应用 要求利用axios实现之前利用AJAX实现的验证用户是否登录的案例 鉴于这两种语法的相似性,只需要在AJAX里面的注册界面里面的script标签里面包含的代码修改为如下代码即可 ...

  3. ISCTF 2022

    Re SigninReverse ida 64 位 打开程序,即可获得flag ISCTF{27413241-9eab-41e2-aca1-88fe8b525956} ezbase # coding= ...

  4. 顺应潮流,解放双手,让ChatGPT不废话直接帮忙编写可融入业务可运行的程序代码(Python3.10实现)

    众所周知,ChatGPT可以帮助研发人员编写或者Debug程序代码,但是在执行过程中,ChatGPT会将程序代码的一些相关文字解释和代码段混合着返回,如此,研发人员还需要自己进行编辑和粘贴操作,效率上 ...

  5. 人人皆可虚拟,直播还能这么玩?声网推出 MetaLive 元直播解决方案

    视频群聊.在线社交.电商带货.游戏竞技.......越来越多的场景融入了直播这一功能.无可厚非,直播可以拉近人与人间的距离,让彼此间的交流更具象.但传统直播场景更为强调主播个人的表现,用户多以围观.刷 ...

  6. 【Unity3D】常用快捷键

    1 单键 Q:扒手拖动(Scene) W:移动(GameObject) E:旋转 R:三维缩放(GameObject 不会变形) T:单维缩放(GameObject 会变形) Y:平移.旋转.缩放 F ...

  7. 你需要知道的 14 个常用的 JavaScript 函数

    1.确定任意对象的具体类型 众所周知,JavaScript 中有六种原始数据类型(Boolean.Number.String.Null.Undefined.Symbol)和一个对象数据类型.但是你知道 ...

  8. 当transcational遇上synchronized

    工作当中经常会遇到既需要开启事务管理,同时也需要同步保证线程安全的场景. 比如一个方法 @Transactional public synchronized void test(){ // } 不知道 ...

  9. MySQL 中索引是如何实现的,有哪些类型的索引,如何进行优化索引

    MySQL 中的索引 前言 索引的实现 哈希索引 全文索引 B+ 树索引 索引的分类 聚簇索引(clustered index) 非聚簇索引(non-clustered index) 联合索引 覆盖索 ...

  10. 异步注解@Async使用及其部分调优

    对于spring异步注解@Async的使用: 对于异步方法调用,从Spring3开始提供了@Async注解,该注解可以被标注在方法上,以便异步地调用该方法.调用者将在调用时立即返回,方法的实际执行将提 ...