题目描述
缓存是一种提高数据库查询效率的手段,现使用训练数据进行模拟访问,以对缓存机制进行优化。
模拟访问规则如下:
- 当查询的数据在缓存中时,直接访问缓存,无需访问数据库。
- 当查询的数据不在缓存中时,则需要访问数据库,并把数据放入缓存:
若放入前缓存已满,则必须先删除缓存中的一个数据。
给定缓存大小和训练数据(一组数据编号),依次模拟访问这组数据,请分析在此访问规则下最少的数据库访问次数。
解答要求
时间限制:3000ms, 内存限制:256MB
输入
第一行:整数 cacheSize,表示缓存大小,范围为 [1,100]
第二行:整数 num,表示训练数据的个数,范围为 [0,1000]
第三行:num 个整数,表示训练数据的每个数据编号,编号取值范围为 [0,1000]
假设每个数据在缓存中占用的空间都是 1 。
输出
一个整数,代表数据库的最少访问次数。
样例
输入样例 1
2
6
1 2 3 1 2 3
输出样例 1
4
提示样例 1
缓存容量大小为2,依次处理待查询的训练数据:
第一次查询数据1,此时缓存中无此数据,需从数据库中读取,结束后缓存内容为1。
第二次查询数据2,此时缓存中无此数据,需从数据库中读取,结束后缓存内容为1 2。
第三次查询数据3,此时缓存中无此数据,需从数据库中读取;此时缓存已满,必须先删除一个数据(选择删除2),然后把新的数据放入,结束后缓存内容为1 3。
第四次查询数据1,此时缓存中有此数据。
第五次查询数据2,此时缓存中无此数据,需从数据库中读取,并且删除缓存中一个数据,然后放入新的数据,结束后缓存内容为2 3。
第六次查询数据3,此时缓存中有此数据。
一共访问了4次数据库,是最优解。其它缓存方式并非最优解。
输入样例 2
1
4
100 200 100 200
输出样例 2
4
Java算法源码
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.stream.Collectors;
public class QueryDatabaseCache {
// 参照数据
List<Integer> idsList = new ArrayList<>();
// 方案一
private int queryDatabase(int cacheSize, int[] ids) {
idsList = Arrays.stream(ids).boxed().collect(Collectors.toList());
int idsLength = ids.length;
if (idsLength <= 1) {
return idsLength;
}
// 缓存存储
List<Integer> cacheList = new LinkedList<>();
// 数据库查询次数
int queryDatabaseNum = 0;
for (int i = 0; i < idsLength; i++) {
int id = ids[i];
if (cacheList.contains(id)) {
continue;
}
// 缓存没有满
if (cacheList.size() < cacheSize) {
cacheList.add(id);
} else { // 缓存满了
int removeIndex = deleteItem(cacheList, i);
cacheList.remove(removeIndex);
cacheList.add(id);
}
queryDatabaseNum++;
}
return queryDatabaseNum;
}
private int deleteItem(List<Integer> cacheList, int index) {
int maxIndexToDelete = 0;
int deleteIndex = 0;
for (int i = 0; i < cacheList.size(); i++) {
int nextIndex = nextIndexOf(idsList, cacheList.get(i), index);
if (nextIndex == -1) {
return i;
}
if (nextIndex > maxIndexToDelete) {
maxIndexToDelete = nextIndex;
deleteIndex = i;
}
}
return deleteIndex;
}
// 方案二
private int queryDatabase2(int cacheSize, int[] ids) {
Map<Integer, List<Integer>> sequenceMap = new HashMap<>(ids.length);
for (int i = 0; i < ids.length; i++) {
int id = ids[i];
if (!sequenceMap.containsKey(id)) {
sequenceMap.put(id, new ArrayList<>());
}
sequenceMap.get(id).add(i);
}
Map<Integer, Boolean> cache = new HashMap<>(2);
int queryDbTimes = 0;
for (int id : ids) {
sequenceMap.get(id).remove(0);
if (cache.get(id) != null) {
continue;
}
queryDbTimes++;
if (cache.size() < cacheSize) {
cache.put(id, true);
continue;
}
Integer maxId = -1;
Integer val = -1;
for (Integer idKey : cache.keySet()) {
if (sequenceMap.get(idKey).isEmpty()) {
maxId = idKey;
break;
}
if (sequenceMap.get(idKey).get(0) > val) {
maxId = idKey;
val = sequenceMap.get(idKey).get(0);
}
}
cache.remove(maxId);
cache.put(id, true);
}
return queryDbTimes;
}
public static int nextIndexOf(List<Integer> list, int num, int index) {
if (list.lastIndexOf(num) <= index) {
return -1;
}
List<Integer> subList = list.subList(index + 1,list.size());
return index + 1 + subList.indexOf(num);
}
public static void main(String[] args) {
Scanner cin = new Scanner(System.in, StandardCharsets.UTF_8.name());
int size = cin.nextInt();
int length = cin.nextInt();
int[] ids = new int[length];
for (int i = 0; i < length; i++) {
ids[i] = cin.nextInt();
}
cin.close();
int maxSum = new QueryDatabaseCache().queryDatabase(size, ids);
System.out.println(maxSum);
System.out.println(new QueryDatabaseCache().queryDatabase2(size, ids));
}
}