一、二叉树(60分)
  给你一个整数序列,用这些数构成一个完全二叉排序树,输出此二叉树的层序遍历序列。
  输入的第一行是一个整数n,表示这个整数序列的长度,输入的第二行包含n个整数,每个数代表完全二叉排序树的一个节点,现在需要输出由这n个整数构成的完全二叉排序树的层序遍历序列。

输入样例:

18
56 987 -25 0 1021 -238 399 12 77 -1 72190 -25 -168 -41367 3218 12 0 -25

输出样例:

12 -1 987 -25 0 77 3218 -238 -25 0 12 56 399 1021 72190 -41367 -168 -25

样例解释:
  输入样例对应的完全二叉排序树如下图所示

思路分析
  二叉排序树有一个特点,它的中根序列是非递减的,因此如果对输入的序列进行非递减排序,那么就相当于得到了这棵树的中根遍历序列,然后再根据这棵树是完全二叉树,就可以得到此树的具体结构。当然,对于此题来说,我们完全没有必要真的将树的结构重构出来,我们只要能够想办法得到它的层序遍历序列即可。
  假设现在有一棵节点数目为 n n n的完全二叉排序树,它的中序遍历序列的第一个元素在数组中的下标是 l I d x lIdx lIdx,那么我们可以做如下数学演算。

  1. 树的高度 h = ⌊ l o g 2 n ⌋ h=\lfloor log_2n \rfloor h=⌊log2​n⌋,高度从0开始。
  2. 除去最后一层,树的其余节点的数目 r e m a i n = 2 h − 1 remain=2^h-1 remain=2h−1。
  3. 树的最后一层的节点数目 l a s t = n − r e m a i n last=n-remain last=n−remain。
  4. 理论上,高度为h的完全二叉树的最后一层可以有的节点数目 l a s t M a x = 2 h lastMax=2^ h lastMax=2h。
  5. 记左子树的节点数目为 l N u m lNum lNum。
  6. 若 l a s t ≥ l a s t N u m / 2 last \ge lastNum/2 last≥lastNum/2,则说明左子树是满二叉树, l N u m = 2 h − 1 lNum=2^h-1 lNum=2h−1。
  7. 若 l a s t < l a s t N u m / 2 last < lastNum/2 last<lastNum/2,则说明左子树的最后一层没有填满, l N u m = 2 h − 1 − 1 + l a s t lNum=2^{h-1}-1+last lNum=2h−1−1+last
  8. 根节点在数组中的下标 r o o t I d x = l I d x + l N u m rootIdx=lIdx+lNum rootIdx=lIdx+lNum。
  9. 左子树中序遍历序列的第一个元素在数组的下标是 l I d x lIdx lIdx,长度为 l N u m lNum lNum。
  10. 右子树中序遍历序列的第一个元素在数组的下标是 r o o t I d x + 1 rootIdx+1 rootIdx+1,长度是 n − l N u m − 1 n-lNum-1 n−lNum−1。

  通过上述的数学演算,可以根据中序遍历序列求出这棵完全二叉排序树的根节点,因为现在要求的是层序遍历序列,所以不能用递归的方法求左子树和右子树的根节点,而应该用队列来求。

源代码

#include <bits/stdc++.h>
using namespace std;
int arr[105];
queue < pair<int, int> > q;//求在arr数组中,从lIdx开始,长度为n的序列所构成的完全二叉排序树的根节点
void root(int lIdx, int n) {if (n <= 0)return;int h;          //树的高度,从0开始int lNum;       //左子树节点数目int remain;     //除去最后一层,其余节点的数目int last;       //最后一层的节点数目int lastMax;    //理论上,最后一层可以有的最大节点数目int rootIdx;    //根节点在数组中的下标h = log2(n);//在由float转为int的过程中,会自动实现向下取整remain = pow(2, h) - 1;last = n - remain;lastMax = pow(2, h);if (last >= lastMax / 2)lNum = pow(2, h) - 1;elselNum = pow(2, h - 1) - 1 + last;rootIdx = lIdx + lNum;cout << arr[rootIdx] << " ";q.push(make_pair(lIdx, lNum));q.push(make_pair(rootIdx + 1, n - lNum - 1));
}
int main()
{int n;cin >> n;for (int i = 1; i <= n; i++) cin >> arr[i];sort(arr + 1, arr + n + 1);q.push(make_pair(1, n));while (!q.empty()) {auto top = q.front();q.pop();root(top.first, top.second);}return 0;
}
/*
18
56 987 -25 0 1021 -238 399 12 77 -1 72190 -25 -168 -41367 3218 12 0 -2512 -1 987 -25 0 77 3218 -238 -25 0 12 56 399 1021 72190 -41367 -168 -25
*/

二、栈(40分)
  给你一个函数调用过程,求调用的最大嵌套深度所对应的函数名,并输出调用路径,同时输出这个函数父函数(调用者)的个数和被调用的次数。同一个函数,如果有多条调用路径都可使其嵌套深度最深,则只输出第一条调用路径。
  输入的数据中,若为1 funcName的形式,则表示调用funcName这个函数,若输入的是0,则表示结束对嵌套深度最大的函数的调用。
  先看第一个测试用例。

1 main
1 input
0
1 area
0
1 findA
1 area
0
0
1 findB
1 get
0
0
0

首先是对main函数的调用,调用之前栈中什么也没有,调用之后栈中只有main函数,如下图所示。

main函数调用input函数
input函数调用结束

main函数调用area函数

area函数调用结束

main函数调用findA函数

findA函数调用area函数

area函数调用结束

findA函数调用结束

main函数调用findB函数

findB函数调用get函数

get函数调用结束

findB函数调用结束

main函数调用结束

  从上面的调用过程可以看出,最大嵌套深度是3,对应的函数有两个,分别是area和get。area的调用路径是main-findA,总共有两个函数调用过area,这两个函数分别是input和findA,area总共被调用过两次。get的调用路径是main-findB,总共有一个函数调用过get,这个函数是findB。所以上面的测试用例输出结果应该是:

area main-findA 2 2
get main-findB 1 1

下面再来看一个更复杂一点的测试用例。

1 main
1 input
0
1 mysqrt
0
1 findA
0
1 findB
1 area
1 mysin
0
1 mycos
0
1 mysqrt
0
0
0
1 findC
1 area
1 mysin
0
0
1 mysqrt
1 max
0
0
0
1 output
0
0

  对于这组输入数据,仍然可以用前面画栈结构图的方法来表示调用过程,限于篇幅,我这里就不再画了。大家应该注意到,对于这组数据,函数的调用次数和调用者的个数已经不再相同,这是因为一个函数可能多次调用另一个函数,比如area函数调用了两次mysin函数,那么mysin函数的调用者的个数是1,因为只有area函数对其进行过调用,但是调用次数却是2,因为它被调用了两次。同时还要注意,mysin函数有两条最深调用路径,分别是main-findB-area-mysin和main-findC-area-mysin,按照之前说过的输出规则,对于同一个函数,如果有多条最深调用路径,只输出第一条,所以最终的输出结果如下。

mysin main-findB-area 1 2
mycos main-findB-area 1 1
mysqrt main-findB-area 3 3
max main-findC-mysqrt 1 1

思路分析
  这道题虽然看起来像是考察栈,但是却不能直接用C++STL中的stack,因为需要记录栈中的内容,用stack操作起来会非常麻烦,所以最终我选择用vector<string>来模拟栈,变量depth相当于栈顶指针,当发生函数调用时,先将被调函数入栈,再令depth自加,当调用结束时,直接令depth自减。
  因为本题是一道模拟题,不涉及复杂的算法,所以不再进行过多的阐述,我已经在代码中加了详细的注释,读者可以边阅读代码,边体会解题过程。

源代码

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
vector<string> callStack; //相当于栈,存放函数名
unordered_map<string, int> numOfCalls;    //调用次数
unordered_map<string, unordered_set<string> > numOfCaller;  //统计有多少个父函数,用unordered_set的目的是去重
vector<vector<string> > ans;    //存放最终结果int main() {callStack.resize(205);int type;string funcName;int depth = 0, maxdep = 0; //分别是当前的调用深度和目前为止最大的调用深度while (cin >> type) {if (type == 1) {//函数调用cin >> funcName;numOfCalls[funcName]++;  //记录函数被调用的次数if (depth != 0) {//深度不为0,说明当前被调用的函数一定有调用者(如果当前被调用的函数是main,则无调用者)string caller = callStack[depth - 1];numOfCaller[funcName].insert(caller);//将funcName函数的所有调用者都保存起来}callStack[depth++] = funcName;//先将funcName入栈,再令调用深度加1if (depth > maxdep) {//更新最大深度ans.clear();ans.push_back(callStack);maxdep = depth;}else if (depth == maxdep) {//最大深度相同int flag = false;   //判断是否有同一个函数,存在多条最深路径的情况for (auto v : ans) {if (v[maxdep - 1] == funcName) {flag = true;break;}}if(flag == false)ans.push_back(callStack);}}else {//调用结束depth--;}}for (int j = 0; j < ans.size(); j++) {funcName = ans[j][maxdep - 1];   //嵌套深度最大的函数cout << funcName << " " << ans[j][0];for (int i = 1; i < maxdep - 1; i++)  //输出调用路径cout << "-" << ans[j][i];cout << " " << numOfCaller[funcName].size() << " " << numOfCalls[funcName] << endl;}return 0;
}

2020北航计算机夏令营机试题目讲解相关推荐

  1. 2020北航计算机夏令营机试题目个人理解

    一.二叉树(60分) 给你一个整数序列,用这些数构成一个完全二叉排序树,输出此二叉树的层序遍历序列.输入的第一行是一个整数n,表示这个整数序列的长度,输入的第二行包含n个整数,每个数代表完全二叉排序树 ...

  2. 人民大学计算机夏令营试题,中国人民大学信息学院2017年夏令营机试题目(考生回忆版)...

    中国人民大学信息学院2017年夏令营机试题目(考生回忆版) (6页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 11.90 积分 中国人民大学信息学院20 ...

  3. 中科大计算机科学夏令营,2019中科大计算机夏令营机试

    2019中科大计算机夏令营机试 今年是中科大计算机夏令营第一次增加上机考试,而且在离开营一个星期的时候才进行通知.极其短暂的准备时间,加上未知的不确定性,还是带来了不小的挑战.听学长说前些年科大夏令营 ...

  4. 2020复旦大学计算机夏令营机试题

    2020复旦大学计算机夏令营机试题 这个是开卷编程,不计入成绩,但是面试会问你做的情况.

  5. 2018南京大学计算机夏令营机试

    1. Count number of binary strings without consecutive 1's Given a positive integer n(3≤n≤90), count ...

  6. 计算机复试机试题目与答案,二零一四年华中科技大学计算机研究生复试机试题目...

    2014年华中科技大学计算机研究生复试机试题目 2014年研究生复试终于结束了,因为自己寻找往年机试题目的经历很艰难,故我希望为之后的学弟,学妹们提供一些资料,以供参考. 今年机试题目总共有3道,编译 ...

  7. 2020北航计算机夏令营

    自己在保研过程中通过查阅学长学姐的面经贴,收获很大.所以自己也写一个夏令营总结,希望能帮助到后面的学弟学妹们. 北航的夏令营感觉机制非常成熟,也很有目的性,上来没有很多余的工作,就是机试,然后面试,然 ...

  8. 记2018年北理计算机夏令营+机试

    个人情况: 2015级 山东大学 成绩排名13/294, 综合排名11/294,四六级552/476 竞赛 包括英特尔杯三等奖,数字媒体科技作品竞赛三等奖,和山东省大学生软件大赛一等奖 科研 没有论文 ...

  9. 2020同济大学电子与信息工程学院计算机系夏令营机试题目【含题解、注释】

    第一题:签到题(10分) [题目要求]: 键盘依次输入 4 个字符串,分别是临时学号.姓名.报考类别和本科学校,按右对齐方式输出,要求其中最长的字符串输出时左边无空格,4 行的冒号对齐,冒号为半角字符 ...

最新文章

  1. ORACLE数据库对比表结构
  2. 搜索引擎, 请手下留情
  3. BH38-Z06单精度绝对值旋转编码器数据手册
  4. Introduction mybatis
  5. 蓝桥杯2015初赛-加法变乘法-枚举
  6. Git教程学习总结(分享给热爱学习的你,团队的协作离不开你呀)
  7. 初学Struts遇到的坑爹问题
  8. php mysql memcache_php的memcache和memcached扩展区别
  9. 一张图解释什么是遗传算法_什么是智慧水务决策支持系统?从一张图说起
  10. ISO14001环境管理体系问答篇
  11. Gatekeeper:首个开源的DDoS防护系统
  12. 嵌入式软件开发成功的 3 个要素
  13. subst ( 将任意目录挂载成虚拟磁碟机 )
  14. 电子取证技术--概述
  15. HCIA网络基础18-PPPoE
  16. 仿射变换affine和透视变换
  17. 乐山持点科技:抖音极速版电商入驻指南
  18. 向量的2-范数、矩阵的2-范数
  19. 无符号数与有符号数比较大小
  20. 关于电脑主机名更改,oracle登录不了的问题

热门文章

  1. 2023年AP微观经济学考什么?
  2. 用Scrapy和Selenium爬取动态数据
  3. [UVa 1646] Edge Case
  4. 2019年实体店怎么用小程序赚钱(上)
  5. 目标检测算法——SSD详解
  6. 当编辑内容改变是后 选中内容 Range 的startOffset会被重置
  7. linux的./configure --prefix的作用
  8. Docker(容器) 部署安装
  9. 作者领读 | Prometheus云原生监控
  10. DLL 注入的三种方法详解