福哥答案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. python之自动化连连看脚本-第一关不动-小记

    (如想转载,请联系博主或贴上本博地址) 仅供学习python之用,勿用做商业用途.运行环境为1920*1080屏幕,python3.7,win7,谷歌浏览器版本 75.0.3770.100. 参考ht ...

  2. shell多进程并发数控制

    在批量执行任务时,单进程执行速度太慢,使用&不加数量控制,又担心资源占用过多,导致宕机等问题,因此我们需要控制并发进程的数量,保证效率的同时,保证资源占用不会太高. 其中一个解决思路是利用简单 ...

  3. margin:auto实现盒子水平垂直居中

    margin:auto为什么不垂直居中 margin:auto是具有强烈计算意味的关键字,用来计算元素对应方向上应该获得的剩余空间大小. 行内元素margin:auto; 不能水平居中在一行的中央位置 ...

  4. 02-Spring基于XML的Bean属性注入

    属性值注入:就是给属性赋值 创建一个Account类: public class Account implements Serializable { private int aid; private ...

  5. JDK1.8中的时间处理API

    相比于JDK1.8之前的SimpleDateFormat以及Calendar等API带来的易误用.线程不安全等问题,JDK1.8提供了LocalDate,LocalTime,LocalDateTime ...

  6. 痞子衡嵌入式:内存读写正确性压力测试程序(memtester)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是内存读写正确性压力测试程序memtester. 在嵌入式系统中,内存(RAM)的重要性不言而喻,系统性能及稳定性都与内存息息相关.关于内 ...

  7. linux网络编程中的errno处理

    在Linux网络编程中,errno是一个非常重要的变量.它记录了最近发生的系统调用错误代码.在编写网络应用程序时,合理处理errno可以帮助我们更好地了解程序出现的问题并进行调试. 通常,在Linux ...

  8. 浅谈js防抖和节流

    防抖和节流是处理高频触发最常见的优化方式,对性能提升有很大的帮助. 防抖:将多次的高频操作优化为只在最后一次执行,应用场景如:输入框,只需在最后一次输入进行校验即可. 节流:保证每隔一段时间只执行一次 ...

  9. mac快捷键和win10快捷键和mma快捷手册

    不定期更新 来自知乎,b站等 mac下的快捷键 如果你mac接了个不一致的键盘,mac会让你检测,(按左ctrl右边的键,按右ctrl左边的键),之后会进行键位映射,这也太复杂了,我拒绝记录. com ...

  10. 使用python自动监控程序运行过程数据

    操作系统 :CentOS 7.6.1810_x64 Python 版本 : 2.7.5 一.背景描述 工作中会遇到需要监控程序运行过程数据的情况,比如定时执行监控的cmd并记录执行结果,本文提供一种实 ...