二叉树是面试中常考的数据结构,因为涉及大量指针操作,因此可以考察思维的严谨性和灵活性。但是校招中的二叉树题规律性很强,因此需要总结一下。
各种常见的二叉树概念
二叉树:每个结点最多有两个子树(左子树和右子树)的树结构。
节点的层次:一个结点的层次直观上来说就是其所在的行,其中根结点层次为1。
二叉树的深度:二叉树的深度指二叉树的最大叶子结点所在的层。二叉树的深度求解可用递归方式实现,等于max(左子树深度,右子树深度)+1。
结点的度:二叉树结点的度指该结点分支的个数,取值为0、1或2。
结点种类:
- 根结点:第一层的结点
- 叶子结点:度为0的结点
- 分支结点:度不为0的结点
- 孩子结点:结点的子树的根称为该结点的孩子结点
- 兄弟结点:同一双亲的孩子结点
- 祖先结点:从根到该结点上所经分支的所有结点
- 子孙节点:以某结点为根的子树中任一结点都称为该结点的子孙节点
建立一棵二叉树
class
二叉树的分类
满二叉树:除叶结点外每一个结点都有左右子结点并且叶子结点都处在最底层的二叉树。
完全二叉树:除最后一层外,其他各层的结点数都达到最大,最后一层的叶子结点都是从左往右依次排布。
二叉排序树(BST):或者是一棵空树,或者具有如下性质:(1)若左子树不为空,则左子树上所有结点的值均小于它的根结点的值;(2)若右子树不为空,则右子树上所有结点的值均大于它的根结点的值;(3)左右子树也分别为二叉排序树;(4)没有值相等的结点。
平衡二叉树(AVL):一种二叉排序树,其中每个结点的左子树和右子树的高度差至多等于1。要么它是一棵空树,要么它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。
二叉树的遍历
二叉树最重要的是四种遍历算法:前序遍历(递归和非递归)、中序遍历(递归和非递归)、后序遍历(递归和非递归)和层次遍历。
前序遍历(根左右)
先访问根结点,然后访问左子树,再访问右子树。
递归算法:
def
非递归算法:
使用栈来临时存储节点:(1)打印根结点数据;(2)把根结点的right入栈,遍历左子树;(3)遍历完左子树返回时,栈顶元素应为right,出栈,遍历以该结点为根的子树
def
中序遍历(左根右)
递归算法:
def
非递归算法:
使用栈来存储临时节点,(1)先将根节点入栈,遍历左子树;(2)遍历完左子树返回时,栈顶元素应为根节点,此时出栈,并打印节点数据;(3)再中序遍历右子树。
def
后序遍历(左右根)
递归算法:
def
非递归算法:
使用栈来存储临时节点,使用哈希表来记录状态。
假设root时遍历树的根指针,后序遍历要求在遍历完左、右子树后再访问根,需要判断根结点的左、右子树是否均遍历过。使用标记法,节点入栈时,将该结点存入哈希表,值为0表示遍历左子树前的现场保护,值为1表示遍历右子树前的现场保护。
首先将root,遍历左子树;返回后,修改哈希表中该结点的值为1,遍历右子树,最后访问根节点。
def
层次遍历
层次遍历需要使用一个辅助队列,初始元素是根结点。当队列不为空时,每次打印队列最前端结点的值,然后判断该结点左子结点是否存在,存在就压入队列;再判断该结点右子节点是否存在,存在就压入队列。
def
二叉树遍历的应用
分层打印二叉树
在层次遍历的基础上,使用两个额外的变量分别保存当前层需要打印的结点数和下一层的结点数。
def
之字形打印二叉树
def
判断二叉树是否为完全二叉树
算法:
- 如果为空树,直接返回True;
- 采用层次遍历,使用一个变量leaf(默认为0);
- 如果当前结点有右孩子,且没有左孩子,直接返回False;
- 如果当前结点没有两个孩子,那么leaf=1,之后的节点必须都为叶节点,即不能有孩子,否则返回False;
- 遍历结束后没有返回,那么返回True。
def