题目
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
解析
预备知识
树的深度为根节点到叶子节点中所有的路径长度最长的值。如图:
图中根节点到叶子节点最长的路径为:1->3->5->6
,所以深度为4.
思路一
我们很容易想到一个树深度为左子树和右子树深度的最大值 + 1。所以问题规模转变为求左子树和右子树的深度,问题形式不变,规模变小,典型的递归问题。所以很容易写出如下精简的代码:
public static int TreeDepth4(TreeNode root) {
if(root == null) {
return 0;
}
int left = TreeDepth4(root.left);
int right = TreeDepth4(root.right);
return Math.max(left, right) + 1;
}
思路二
我们都知道递归在问题规模很大的时候容易出现栈溢出,所以我们尝试换种思路来避免递归。
在树的遍历中,实现简单而不是递归的写法就属层序遍历了。
层序遍历:顾名思义,就是从根节点一层一层的遍历整棵树。
我们可以按层来处理,每遍历一层,深度加一,直到遍历完整棵树,即可得到层数(即深度)。层序遍历也就是BFS非常适合用先进先出的队列来充当容器,深度初始为0,根节点先入队,然后处理第一层,深度加1,并把第二层的节点加入;之后继续处理第二层,深度加1,获取当前队列的大小,此时队列的大小即为当前层节点总数size,依次从队列中取出大小为size的节点,期间并把这些节点的孩子节点加入(即下一层的);重复以上步骤,直到队列为空,表明遍历到最底层。
public static int TreeDepth(TreeNode root) {
if(root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int depth = 0;
while(!queue.isEmpty()) {
int currentLayerSize = queue.size();
depth++;
for(int i = 0; i < currentLayerSize; i++) {
TreeNode node = queue.poll();
if(node.left != null) {
queue.offer(node.left);
}
if(node.right != null) {
queue.offer(node.right);
}
}
}
return depth;
}
思路三
思路三跟思路二一样,只不过按层处理的策略不同。
思路二是通过每次把当前层的节点弹出来确保最外层每次访问都是树的每一层。
而思路三是借用2个指针last,nLast。其中last指向当前层的最后一个节点,nLast指向下一层的最后一个节点。
1. 初始last指向根节点,root进栈。
2. 开始循环体,判断栈是否为空,不为空,则弹出节点node,继续步骤3;否则步骤5
3. 判断node的左右孩子是否不为空,若不为空,则入队,并更新nLast指向孩子。
4. 判断node是否等于last,若是,则表明下一次该访问下一层了,所以深度加1,并把last更新为nLast
5. 结束循环,返回深度
public static int TreeDepth3(TreeNode root) {
if(root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int depth = 0;
TreeNode last = root, nLast = null;
while(!queue.isEmpty()) {
TreeNode node = queue.poll();
if(node.left != null) {
queue.offer(node.left);
nLast = node.left;
}
if(node.right != null) {
queue.offer(node.right);
nLast = node.right;
}
if(node == last) {
depth++;
last = nLast;
}
}
return depth;
}
总结
有序数组的查找首选二分查找。