2016校招真题解析(一)小米git
2017-06-29 16:15
316 查看
题目描述
git是一种分布式代码管理工具,git通过树的形式记录文件的更改历史,比如: base'<--base<--A<--A' ^ | --- B<--B' 小米工程师常常需要寻找两个分支最近的分割点,即base.假设git树是多叉树,请实现一个算法,计算git树上任意两点的最近分割点。 (假设git树节点数为n,用邻接矩阵的形式表示git树:字符串数组matrix包含n个字符串,每个字符串由字符'0'或'1'组成,长度为n。matrix[i][j]=='1'当且仅当git树种第i个和第j个节点有连接。节点0为git树的根节点。)
输入例子
[01011,10100,01000,10000,10000],1,2输出例子
1审题观察
这道题将分布式代码管理工具git作为出题背景,通过读题,答题者应该能迅速锁定所要考察的数据结构——树。题目要求“找出git树上任意两点的最近分割点”,
那么这道题实际上就是求多叉树中任意两个节点最近的共同祖先。
理解了题目要求,接下来就要观察输入的形式,以此来决定如何处理输入。题干的后半部分指出,多叉树是使用邻接矩阵表示,那么答题者应当迅速回忆起如何通过邻接矩阵,
获取关于这棵树的结构信息,例如找到某个指定节点的父节点、进行遍历操作或从某个指定节点一直上溯到根节点等。这个要求答题者平时就熟练掌握,才能正确快速地运用到解题当中。
题目给出了输入例子和输出例子,实际上这个例子并不具有一般性,是个特例,也就是节点1和节点2最近的共同祖先是节点1本身,这个提醒了答题者——是否要考虑某些特例?
例如,共同祖先可不可以是节点自身?题目恰好给出了这个特例,告诉我们可以;会不会有无解的情况存在?不会,因为根节点总会是这两个节点的共同祖先。
从以上三个方面审题和观察后,下面就可以确定解题思路了。
解题思路
从审题观察中,我们可以获知,关键要求是“最近的共同祖先”。进一步分解为以下两个要求:
1.要求的这个节点既是节点A的祖先(可以是节点A本身),也是节点B的祖先(可以是节点B本身);
2.要求这个节点是所有满足1的节点中,距离A和B最近的。
实际上,这类要求并不罕见,很多算法问题都具有类似“公共”+“最优”这样的组合限制条件。解决这类问题的思路往往是:
1.分别寻找——找出节点A的所有祖先节点和节点B的所有祖先节点。实际上,这些节点都在从根节点到节点A(或节点B)的路径上,所以分别找出从根节点到节点A和根节点到节点B的路径即可;
2.合而为一——在两条路径中,按照距离根节点从远到近一一比对,发现第一个公共的节点,就是所要求的节点。
算法设计
基于上述解题思路,我们可以按步骤设计算法。但是在找到从根节点到节点A(或节点B)的路径之前,我们还需要处理一下邻接矩阵,来获知树的结构。
这里我们要从邻接矩阵中解析出所有直接的父子节点关系:
int len = matrix.length;
// 构造一个father数组,存放每个节点的父节点
int[] father = new int[len];
// 标志数组
int[] flag = new int[len];
// 根节点的父节点为-1
father[0] = -1;
// 根节点 已经访问过
flag[0] = 1;
Queue<Integer> children = new ArrayDeque<>();
children.offer(0);
// 构造father数组,从根节点0开始
while (!children.isEmpty()) {
int parent = children.poll();
char[] chars = matrix[parent].toCharArray();
for (int i = 0; i < chars.length; i++) {
if (flag[i] != 1 && chars[i] == '1') {
// 设置父节点
father[i] = parent;
// 将其加入孩子队列
children.offer(i);
// 标记为 访问过
flag[i] = 1;
}
}
}这里的关键点有两个:1)题目当中指出节点0为git树的根节点,所以要对根节点作特殊处理,并将根节点首先入队;2)使用队列进行树的逐层遍历,这是树遍历的基本代码,答题者平常就应该熟练掌握。需要注意的时,这里使用了一个标记数组,来防止重复考察某个节点。
有了记录所有节点对应的父节点的father数组之后,接下来就可以利用这个数组获取根节点到节点A或节点B的路径,我们可以用队列来保持先后顺序:
int ia = indexA;
int ib = indexB;
// 记录从根节点到本节点的路径
Deque<Integer> queueA = new ArrayDeque<>();
Deque<Integer> queueB = new ArrayDeque<>();
while (ia != -1) {
queueA.addFirst(ia);
ia = father[ia];
}
while (ib != -1) {
queueB.addFirst(ib);
ib = father[ib];
}有了这样的两条路径(用两个队列表示),接下来要求最近的公共祖先就方便了,正好可以利用队列先进先出的特性:
// 找到公共父节点
int commonParent = 0;
while (queueA.peekFirst() == queueB.peekFirst()) {
commonParent = queueA.peekFirst();
queueA.pollFirst();
queueB.pollFirst();
}
return commonParent;【注】
1)本题题目来自牛客网,代码解答选自牛客网网友——惨绿青年的解答,如有侵权,可联系博主进行处理。
相关文章推荐
- 2016校招真题-小米Git(找最近双亲)
- 【newCoder】2016校招真题在线编程 小米Git
- 笔试真题解析 2016 SYDZ 校招在线笔试题
- 2016校招真题之顺时针旋转矩阵
- 2016校招真题之数组单调和
- 2016校招真题之二维数组打印
- LeetCode 123. Best Time to Buy and Sell Stock II,小米2016校招 风口的猪-中国牛市
- 2016校招真题汇总1:最高分是多少
- 2015小米校招【研发类】面试真题经验分享
- 2016校招真题之小球的距离
- 2016小米校招笔试题
- 【newCoder】2016校招真题在线编程 懂二进制
- 笔试真题解析 ALBB-2015 校招研发在线笔试题
- 【校招真题】2016 美团 最大差值
- 2016校招真题编程练习——微信红包(腾讯)
- 2016校招真题在线编程 懂二进制
- 2016联想校招图形图像软件工程师在线笔试真题
- 2016联想校招软件开发类在线笔试真题
- 风口的猪-中国牛市(小米2016校招)
- 2016校招真题汇总3:扑克牌大小