今天面试,被问到了二叉树的深度计算问题。
一听到这个问题,第一反应是大学被写烂了的基本数据结构问题。
然而我已经毕业3年,没写算法很久,手都生了。但是呢,不能说生了就只对面试官说思路吧,于是还是不假思索的立马拿起笔,写了起来,毕竟这是一个基本的数据结构题目。
首先立马写了一个二叉树结构。
public class BinaryTree{
BinaryTree lChild, rChild;
int value;
}
一写出来,立马有了感觉。顺势写了计算方法
public int computeTreeDepth(BinaryTree binaryTree){
if(binaryTree.lChild == null && binaryTree.rChild == null){
return 1;
}
int lDepth = computeTreeDepth(binaryTree.lChild);
int rDepth = computeTreeDepth(binaryTree.rChild);
return Math.max(lDepth, rDepth);
}
然后就给面试官看了。
面试官问我有感觉哪里不对吗?哎,只怪我太久没写算法题了,连这个基本的深度计算,都出现简单失误。更可气的是在当时的紧张环境下,我竟然没有发现哪里问题。后来经过面试官提醒,也终于改了过来。
public int computeTreeDepth(BinaryTree binaryTree){
if(binaryTree == null){
return 0;
}
if(binaryTree.lChild == null && binaryTree.rChild == null){
return 1;
}
int lDepth = computeTreeDepth(binaryTree.lChild) + 1;
int rDepth = computeTreeDepth(binaryTree.rChild) + 1;
return Math.max(lDepth, rDepth);
}
此时我的心里一万个对自己无语,竟然在面试的时候出现这么大的失误(不过也只怪我平常自从毕业就很少去深入写算法题了,以后一定要注意)
这个时候,面试官说这是递归实现,写一下非递归实现。
当时心里无语的我顿时一塞,非递归实现,然后一想,也简单。然后就屁颠屁颠的开始写起来。写着写着我发现,情况似乎不是我想的这么简单。随着时间的深入,我的心里变得异常紧张,结果硬是在那几分钟没想出来。此刻真是深深的鄙视自己,无论是什么原因让我当时的大脑短路了,终究的结果是我没写出来非递归实现。之后面试官委婉的叫我下去在思考这个问题吧。
总结一下:
问题:自己当时太想着展现自己,导致自己的思路太过紧凑反而限制了我的思维,导致这个简单的问题也写出漏洞,甚至连非递归实现最后没编写出来。
对自己的思考:
1.作为程序猿,平常还是要多锻炼自己的算法编写习惯,多去思考。只有这样才可以在面试中对算法题有一个较好的把控。
2.面试的时候心态要放平稳,不能心浮气躁,导致自己出现各种临时小问题。因为对方只会看结果。
回来了,自己重新写了一遍二叉树的深度计算实现,果然很简单。记录一下吧,就当是自己前进的路上的一个绊脚石,也希望自己在每一次总结之后能进步自己。
多思考,多学习,放平心态,去面对每一个新技术,新思维。
下面贴代码,以及思路
二叉树深度计算的递归实现:
public int computeTreeDepth(BinaryTree binaryTree){
if(binaryTree == null){
return 0;
}
return Math.max(computeTreeDepth(binaryTree.lChild), computeTreeDepth(binaryTree.rChild)) + 1;
}
二叉树深度计算的非递归实现:
首先想到是直接遍历,如果是直接遍历那自然就有两种思路,广度优先和深度优先。
先说广度优先,因为是遍历下一层意味着上一层都已遍历,先进先出,采用队列实现:
public int computeTreeDepth(BinaryTree binaryTree){
//实例化队列
Queue<BinaryTree> mVisitList = new LinkedList<>();
BinaryTree tmp;
int depth = 0, lengthTmp;
if(binaryTree == null){
//根为空,直接返回0
return 0;
}
//将根加入队列
mVisitList.offer(binaryTree);
while (!mVisitList.isEmpty()){
//只要队列不为空,说明新的一层数据不为空,且已经加到队列,深度+1
depth ++;
//遍历该层到所有数据,将他们出队,并附带把所有下一层到数据都加进来(如果有的话)
lengthTmp = mVisitList.size();
while (lengthTmp -- > 0){
tmp = mVisitList.poll();
if(tmp.lChild != null){
mVisitList.offer(tmp.lChild);
}
if(tmp.rChild != null){
mVisitList.offer(tmp.rChild);
}
}
}
return depth;
}
然后深度优先,因为是深度遍历那肯定是先进后出,采用栈实现,然后遍历思路是先遍历当前节点的左子树,没有左子树到情况在遍历右子树:
public int computeTreeDepth(BinaryTree binaryTree){
//实例化栈
Stack<BinaryTree> mVisitList = new Stack<>();
BinaryTree tmp = binaryTree;
int depth = 0;//遍历过程中到最大深度
while (tmp != null){
if(tmp.lChild != null || tmp.rChild != null){
//如果有子树,将当前节点入栈,且更新最大深度
mVisitList.push(tmp);
depth = Math.max(depth, mVisitList.size());
//因为是左子树优先,所以深度遍历下一个子节点的时候,优先左子树
tmp = tmp.lChild != null ? tmp.lChild : tmp.rChild;
continue;
}
//当前节点没有子树,直接更新最大深度(访问到当前节点到深度是栈的深度+1)
depth = Math.max(depth, mVisitList.size() + 1);
//此时回溯去找右子树
while (!mVisitList.isEmpty()){
if(mVisitList.peek().rChild != null){
//如果栈顶节点的右子树不为空,访问右子树
//访问的时候注意,如果栈顶节点的右子树等于当前正在访问的节点,应该置空避免没必要的循环
tmp = mVisitList.peek().rChild == tmp ? null : mVisitList.peek().rChild;
break;
}
//说明当前栈顶节点的右子树为空,直接出栈,继续回溯
// 且要更新当前节点,用于记录当前正在回溯的节点,避免死循环回溯
tmp = mVisitList.pop();
}
}
return depth;
}
好了,上面就是二叉树的实现,自己写了几个测试用例没出现问题。但是不能保证完全没问题,有问题的话或者可以优化的欢迎留言交流,一起学习进步。
最后,希望我的总结也可以让你在接下来的工作中或者学业上也有所帮助。
网友评论