Day38:二叉树的深度
剑指Offer_编程题——二叉树的深度
题目描述:
输入一颗二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径为树的深度。
具体要求:
时间限制: C/C++ 1秒,其他语言2秒 空间限制: C/C++32M,其他语言64M
具体实现
背景知识介绍 在开始做本题之前我们先给大家介绍**二叉树树的遍历:**先序遍历、中序遍历、后序遍历 以及 层次遍历。二叉树的遍历(traversing binary tree)是指从根结点出发,按照某种次序依次访问二叉树中所有的结点,使得每个结点被访问依次且仅被访问一次。 1、前序遍历,先检查 根节点,然后递归遍历 左子树 和 右子树 2、中序遍历,先遍历 左子树,然后检查 根节点,最后遍历 右子树 3、后序遍历,先递归遍历 左右子树,然后检查 根节点 4、层次遍历,从 第一层开始,一层一层往下遍历 我们以10,5,19,4,8,13,24为例,用动画详细介绍这四种遍历:
1、前序遍历
a:输出当前节点值10,然后输出左子树,最后输出右子树; b、对于其左子树来说,同样以这样的顺序,则先输出5,然后输出左子树4,最后输出右子树8; c、对于其右子树,同样以这样的顺序,则先输出19,然后输出左子树13,最后输出右子树24; 最终得到前序遍历输出为:10,5,4,8,19,13,24。 2、中序遍历
a:先输出左子树,然后输出当前节点10,最后输出右子树; b:对于其左子树来说,同样以这样的顺序,则先输出左子树4,然后输出节点值5,最后输出右子树8; c:对于其右子树,同样以这样的顺序,则先,输出左子树13,然后输出节点值19,最后输出右子树24; 最终得到中序遍历输出为:4,5,8,10,13,19,24。 我们发现 二叉查找树的中序遍历输出就是排序后的结果。因此二叉查找树也叫二叉搜索树或者二叉排序树。 3、后序遍历
a:先输出左子树,然后输出右子树,最后输出节点值10 b:对于其左子树来说,同样以这样的顺序,则先输出左子树4,然后输出右子树8,最后输出节点值5; c:对于其右子树,同样以这样的顺序,则先,输出左子树13,然后输出右子树24,最后输出节点值19 最终得到后序遍历输出为:4,8,5,13,24,19,10 。 5、层次遍历
a:遍历第一层,输出10 b:遍历第二层,输出5,19 c:遍历第三层,输出4,8,13,24 最终得到层次遍历输出为: 10,5,19,4,8,13,24 。 不过虽然看起来过程很简单,但是代码实现却不能像前面三种深度优先遍历方式那样 直接使用递归,它更好的方式是借助一个具有 先入先出特点的队列(队列可参考队列-C语言实现)。以三个节点为例,我们先将根节点入队,然后分别入队左右孩子节点,最后输出队列内容,那么它的顺序就是层次遍历的顺序了。 具体实现 本题主要是求二叉树的深度,其实首先要进行二叉树的遍历,本题可以通过先序遍历、层次遍历、还可以用作递归。具体实现如下: 1、首先我们进行层次遍历
import java.util.*;
public class Solution{
public int TreeDepth(TreeNode root){
int count = 0;
int curCount = 1;
int nextCount = 0;
int totalCount = 0;
if(root == null){
return totalCount;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode temp = queue.remove();
count++;
if(temp.left != null){
nextCount++;
queue.add(temp.left);
}
if(temp.right != null){
nextCount++;
queue.add(temp.right);
}
if(count == curCount){
totalCount++;
count = 0;
curCount = nextCount;
nextCount = 0;
}
}
return totalCount;
}
}
代码效果图如图所示:
正如我们之前的文章中提到,如果在牛客网中我们以上代码可以直接编译通过,因为它给我们定义了节点TreeNode,但如果在本地编译器(IDEA)中,我们得自己定义该节点,具体实现如下:
public class TreeNode{
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val){
this.val = val;
}
}
2、接下来,我们用递归来解决该题,其实任意的二叉树均可以看作root----left----right。具体实现如下:
public class Solution{
public int TreeDepth(TreeNode root){
if (root == null)
return 0;
return(1 + TreeDepth(root.left)) > (1 + TreeDepth(root.right))?(1 + TreeDepth(root.left)):(1 + TreeDepth(root.right));
}
}
代码效果图如图所示:
找到左右子树最大深度+1即可,递归的终止条件是空节点的深度是0。其实与我们刚才的递归算法很类似,具体用python实现如下:
class Solution:
def TreeDepth(self, pRoot):
if not pRoot:
return 0
left = self.TreeDepth(pRoot.left)
right = self.TreeDepth(pRoot.right)
return max(left, right) + 1
代码效果图如图所示:
用python实现TreeNode结构:
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
3、然后我们用先序遍历来解决该题。实际上线找右子树的最大高度,再找左子树的最大高度。当然我们也可以先左子树进栈,然后右子树进栈,也就是先找左子树的最大高度,再找右子树的最大高度。具体实现如下:
import java.util.*;
public class Solution{
public int TreeDepth(TreeNode root){
int max = 0;
if(root == null)
return max;
Stack<TreeNode> stack1 = new Stack<TreeNode>();
stack1.push(root);
Stack<Integer> stack2 = new Stack<Integer>();
stack2.push(0);
while(!stack1.isEmpty()){
TreeNode temp = stack1.pop();
int num = stack2.pop();
num++;
if(num > max)
max = num;
if(temp.left != null){
stack1.push(temp.left);
stack2.push(num);
}
if(temp.right != null){
stack1.push(temp.right);
stack2.push(num);
}
}
return max;
}
}
代码效果图如图所示:
最后一种就是暴力法,具体实现如下:
public class Solution{
int max = 0;
int temp = 0;
public int TreeDepth(TreeNode root){
f(root);
return max;
}
public void f(TreeNode root){
if(root == null){
if (temp > max)
max = temp;
return;
}
temp++;
f(root.left);
f(root.right);
temp--;
}
}
代码效果图如图所示:
总结
本道题主要考察二叉树的深度,本题直截了当,思路清晰。很显然考察的是二叉树的四种遍历以及递归在二叉树中的使用,我们之前的很多二叉树的题目中,均用到了递归,因此,本题给出了四种解法来解决此题,并且在解题之前用动画的形式详细的为大家介绍了二叉树的四种遍历方法。因此,我们在做题的时候,应该多次尝试各种方法,扩展自己的思维,写出优质的代码。总之,我们要继续加油,争取早日找到工作,Good Luck!!!
参考文献
[1] 动画:二叉树遍历的多种姿势 [2] COSCHINA [3] Chosen_Xxx
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Linux中可以节省你时间的15个命令别名
- Linux下利用unzip命令如何解压多个文件详解
- linux查看磁盘使用情况方法
- 详解Linux(centos7)下安装OpenSSL安装图文方法
- 面试官:考你几个简单的事件问题吧
- 如何创建Linux的swap交换分区文件的方法步骤
- Linux利用keepalived实现lvs的高可用性的操作方法
- Canvas系列(11):动画中级
- 使用autoconf生成Makefile并编译工程的步骤
- frida- registernatives获取so层动态注册函数
- Ubuntu下安装并配置VS Code编译C++的方法
- LInux下如何挂载光盘找rpm包的方法步骤
- Linux 改变文件大小的方法
- Canvas系列(9):其他API
- CentOS7将Nginx添加系统服务的方法步骤