第一题、 模拟缓存
理解题意之后直接码代码就可以了。不过在保存缓存的数据结构选择上倒是有小技巧,如果你用普通数组保存,那么在加入新的页面的时候,如果数组还没满,则直接append到最末尾即可;如果缓存已满,则需淘汰第一个页面。淘汰一个旧页面时需要进行的操作是将第二个页面开始到最后一个页面往前覆盖,空出最后一个位置,然后把新页面放在最后一个位置上。这种淘汰的时间复杂度为缓存长度m。有一种改进是直接用队列queue保留缓存的长度。这里我自己使用的小技巧是用几个变量将原来的普通数组改进成一个循环数组的模式,具体代码如下。遗憾的是,使用普通数组所有测试用例全部通过,使用循环数组只能达到13/16,还欠缺考虑,如果发现,麻烦批评指正,不胜感谢。
#include <iostream> using namespace std; int countMissPage(int, int*, int); int main() { int page_requests[] = { 1,2,1,3,1,2 }; int max_cache_size = 2; int len = 6; cout << countMissPage(max_cache_size, page_requests, len) << endl; } int countMissPage(int max_cache_size, int* page_requests, int n) { if (!page_requests || n <= 0) return 0; if (max_cache_size <= 0) return n; int countMiss = 0; int currCacheNum = 0; // 用来记录当前循环数组的头部 int front = 0; // 缓存数组,页面编号全部初始化为0 int* cache = new int[max_cache_size] {0}; for (int i = 0; i < n; ++i) { bool isFind = false; // 查找请求的页面是否在当前缓存中 for (int j = 0; j < max_cache_size; ++j) if (cache[j] == page_requests[i]) { isFind = true; break; } // 请求的页面不再缓存中 if (!isFind) { // 未命中数加一 countMiss++; // 如果缓存数组还未满,直接append在数组末尾 if (currCacheNum < max_cache_size) cache[currCacheNum++] = page_requests[i]; // 如果已满,则将原队首放入新页面,修改下一位为新队首,原队首成为队尾 else { cache[front] = page_requests[i]; front = (front + 1) % max_cache_size; } } } delete[] cache; return countMiss; }
第二题、 模拟最短作业优先
分析题意可知,由于题意中规定cpu不会出现空闲的情况,也即CPU已经处理完前面的作业,下一个作业时间上还未到达。比如第二个测试用例,P4的作业时间为5明显小于P3的7,但是CPU不可能选择P4先处理而让累计cpu时间从3秒(P1和P2的累计作业时间)到9秒(P4的达到时间)中间这6秒空闲不做其他任务而只等待P4。有了这个条件,整道题就简单多了,否则需要考虑如果多个未到达作业,应该选取作业时间短的,还是时间上先到达的,抑或还有其他策略,情况变得很复杂(我就是忽略了cpu不会空闲而踏入坑里啊!),剩下的,代码中有注释:
#include <iostream> using namespace std; float waitingTimeSJF(int*, int*, int); int main() { int requestTime[] = { 0,2,4,5 }/*{ 0,1,3,9 }*/; int durations[] = { 7,4,1,4 }/*{ 2,1,7,5 }*/; int n = 4; cout << waitingTimeSJF(requestTime, durations, n) << endl; } float waitingTimeSJF(int* requestTime, int* durations, int n) { if (!requestTime || !durations || n <= 0) return 0.0f; bool* isVisited = new bool[n] {false}; int totalTime = durations[0]; int waitTime = 0; isVisited[0] = true; int index; for (int i = 1; i < n; ++i) { // 依题意,单个作业服务时间小于100 int minDur = 100; // 对于剩下的n-1个作业,做如下循环:1)是否已经提交过;2)作业是否已经到达;3)选取所有已到达作业的最短作业 for (int j = 0; j < n; ++j) { if (!isVisited[j] && requestTime[j] <= totalTime && durations[j] < minDur) { minDur = durations[j]; index = j; } } waitTime += (totalTime - requestTime[index]); totalTime += durations[index]; isVisited[index] = true; } return (float)waitTime / (float)n; }
第三题、 判断树2是否为树1的子树
这道题忘了截图和具体题意了,大体上跟剑指offer或者其他相关资料的题意差不多,所差“不多”的那点还挺关键:在其他书籍的子树题目中,只要树2的全部数据和结构在树1中出现过,则判定为满足条件,无论树2在树1的那个位置。但是百度这道题的话,依照我的测试,树2一定要是叶子节点与树1的叶子节点相对应才算符合条件。如下图:
情况1返回true,情况2返回false。因此一开始我使用常规的判断是否为子树只能过了case1而过不了case2,按照这个思路稍微修改一下条件之后这两个测试用例都过了,然后所有其他测试用例中过了19个中的17个,还是有一些情况没考虑周全,题意和代码都没留下来,不做测试了。