您的位置:首页 > 职场人生

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)本题题目来自牛客网,代码解答选自牛客网网友——惨绿青年的解答,如有侵权,可联系博主进行处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息