【华为OD机试真题 C语言】481、文件缓存系统 | 机试真题+思路参考+代码解析(C卷)
一、题目
🎃题目描述
请设计一个文件缓存系统,该文件缓存系统可以指定缓存的最大值(单位为字节) 文件缓存系统有两种操作: 1、存储文件(put) 2、读取文件(get) 操作命令为: 1、put fileName fileSize 2、get fileName 存储文件是把文件放入文件缓存系统中; 读取文件是从文件缓存系统中访问已存在,如果文件不存在,则不作任何操作。 当缓存空间不足以存放新的文件时,根据规则删除文件,直到剩余空间满足新的文件大小位置,再存放新文件。 具体的删除规则为: 文件访问过后,会更新文件的最近访问时间和总的访问次数,当缓存不够时,按照第一优先顺序为访问次数从少到多,第二顺序为时间从老到新的方式来删除文件。
🎃输入输出
输入 第一行为缓存最大值 m(整数,取值范围为 0 < m ≤ 52428800) 第二行为文件操作序列个数 n(0 ≤ n ≤ 300000) 从第三行起为文件操作序列,每个序列单独一行,文件操作定义为:op file_name file_size 输出 输出当前文件缓存中的文件名列表,文件名用英文逗号分隔,按字典顺序排序,如:a,c ;如果文件缓存中没有文件,则输出NONE 注意: 1、如果新文件的文件名和文件缓存中已有的文件名相同,则不会放在缓存中 2、新的文件第一次存入到文件缓存中时,文件的总访问次数不会变化,文件的最近访问时间会更新到最新时间 3、每次文件访问后,总访问次数加1,最近访问时间更新到最新时间 4、任何两个文件的最近访问时间不会重复 5、文件名不会为空,均为小写字母,最大长度为10 6、缓存空间不足时,不能存放新文件 7、每个文件大小都是大于 0 的整数
🎃样例1
输入
50
6
put a 10
put b 20
get a
get a
get b
put c 30
输出
a,c
🎃样例2
输入
50
1
get file
输出
NONE
二、思路参考
典型的LFU实现。
三、代码参考
type file struct {
name string // max to 10 low case letters
size int
frequency int // access frequency
accessDate time.Time
index int // the index of item in the heap
}
// PQ is a priority queue implements heap.Interface and holding files.
type PQ []*file
func (pq PQ) Len() int {
return len(pq)
}
func (pq PQ) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].index = i
pq[j].index = j
}
func (pq PQ) Less(i, j int) bool {
if pq[i].frequency == pq[j].frequency {
return pq[i].accessDate.Before(pq[j].accessDate)
}
return pq[i].frequency < pq[j].frequency
}
func (pq *PQ) Push(x any) {
n := len(*pq)
file := x.(*file)
file.index = n
file.accessDate = time.Now()
*pq = append(*pq, file)
}
func (pq *PQ) Pop() any {
old := *pq
n := len(old)
file := old[n-1]
file.index = -1
*pq = old[0 : n-1]
return file
}
func (pq *PQ) update(f *file) {
f.frequency++
f.accessDate = time.Now()
heap.Fix(pq, f.index)
}
type FileCache struct {
// mapping from file name to *file
m map[string]*file
pq PQ
capSize int
currentSize int
fileCount int
accessDate time.Time
}
func NewFileCache(capacity int) *FileCache {
return &FileCache{
m: make(map[string]*file),
// 如果写pq: make([]*file, 8) 则表示cap len都是8,会有8个空元素,切记slice的小坑
pq: make([]*file, 0, 8),
capSize: capacity,
}
}
func (c *FileCache) GetFile(name string) {
f, ok := c.m[name]
if ok {
c.pq.update(f)
}
}
func (c *FileCache) PutFile(name string, size int) {
if c.capSize < 0 {
return
}
_, ok := c.m[name]
if ok {
// 文件存在,不做任何操作
return
}
f := &file{
name: name,
size: size,
}
// 如果容量不足,evict一些文件
if c.currentSize+size > c.capSize {
c.evictFiles(size)
}
// 将文件存入,更新FileCache字段
heap.Push(&c.pq, f)
c.m[name] = f
c.fileCount++
c.currentSize += size
}
func (c *FileCache) evictFiles(reqSize int) {
for c.currentSize+reqSize > c.capSize && c.fileCount > 0 {
f := heap.Pop(&c.pq).(*file)
c.currentSize -= f.size
delete(c.m, f.name)
c.fileCount--
}
}
type ByName struct {
PQ
}
func (by ByName) Less(i, j int) bool {
return by.PQ[i].name < by.PQ[j].name
}
func (c *FileCache) Print() {
if c.fileCount == 0 {
fmt.Println("None")
}
sort.Sort(ByName{c.pq})
for _, f := range c.pq {
fmt.Printf("%s,", f.name)
}
fmt.Println()
heap.Init(&c.pq)
}