遍历二叉树

来源:互联网 发布:grub命令行启动linux 编辑:程序博客网 时间:2024/05/17 12:25
1.  二叉树的遍历
        遍历定义  ——顺着某一条搜索路径巡访二叉树中的结点,使得 每个结点均被访问一次,而且仅被访问一次。
        “访问”的含义可以很广,如:输出结点的信息等。 
        遍历用途——查找具有某种特征的结点;对树中全部结点逐一进行某种处理。遍历是二叉树一切运算的基础和核心。
遍历规则
二叉树由根、左子树、右子树构成,定义为D、 L、R 
D、 L、R的组合定义了六种可能的遍历方案:  LDR,   LRD,   DLR,   DRL,   RDL,   RLD 
若限定先左后右,则有三种实现方案:          DLR                    LDR                     LRD 
先 (根)序遍历       中 (根)序遍历        后(根)序遍历  
 注:“先、中、后”的意思是指访问的结点D是先于子树出现还是后于子树出现。

讨论:若已知先序/后序遍历结果和中序遍历结果, 能否“恢复”出二叉树? 
        例如:已知一棵二叉树的中序序列和后序序列分别是BDCEAFHG 和 DECBHGFA,请画出这棵二叉树。
        分析: 
        ①由后序遍历特征,根结点必在后序序列尾部(即A); 
        ②由中序遍历特征,根结点必在其中间,而且其左部必全部是左子树子孙(即BDCE),其右部必全部是右子树子孙(即   FHG); 
        ③继而,根据后序中的DECB子树可确定B为A的左孩子,根据HGF子串可确定F为A的右孩子;以此类推。

例如:
        已知一棵二叉树的前序遍历序列和中序遍历序列分别为ABCDEFGHI 和BCAEDGHFI,如何构造该二叉树呢? 
解题步骤:

二叉树遍历算法的递归实现:

对遍历的分析: 
        1. 从前面的三种遍历算法可以知道:如果将printf语句抹去,从递归的角度看,这三种算法是完全相同的,或者说这三种遍历算法的访问路径是相同的,只是访问结点的时机不同。

       从虚线的出发点到终点的路径 上,每个结点经过3次。
        第1次经过时访问=先序遍历 
        第2次经过时访问=中序遍历 
        第3次经过时访问=后序遍历 
        2. 二叉树遍历的时间效率和空间效率 
        时间效率:O(n) //每个结点只访问一次 
        空间效率:O(n) //栈占用的最大辅助空间 
        (精确值:树深为k的递归遍历需要k+1个辅助单元!最坏情况深度为n,所以空间复杂度为O(n))
二叉树遍历算法的非递归实现
        算法思路:若不用递归,则要实现二叉树遍历的“嵌套”规则,必用堆栈。




二叉树遍历算法的应用举例

例1  统计二叉树中叶子结点的个数 
思路:输出叶子结点比较简单,用任何一种遍历算法,凡是左右指针均空者,则为叶子,将其统计并打印出来。

二叉树遍历算法的应用举例

例1  统计二叉树中叶子结点的个数 
思路:输出叶子结点比较简单,用任何一种遍历算法,凡是左右指针均空者,则为叶子,将其统计并打印出来。

例3  求二叉树的深度
算法思路:
        只查各结点后继链表指针,若左(右)孩子的左(右)指针非空,则层次数加1;否则函数返回。 
        当T= NULL时,深度为0;
        否则, T的深度= MAX{左子树深度,右子树深度}+1;  
例4  按层次输出二叉树中的所有结点
        算法思路:既然要求从上到下,从左到右,则利用队列存放各子树结点的指针是个好办法,而不必拘泥于递归算法。 
        技巧:当根结点入队后,根据其左右孩子指针域令其左、右孩子结点入队,然后根节点出队; 而之后根结点以外的结点出队时又令它的左右孩子结点入队,……由此便可产生按层次输出的效果。
例5   判断二叉树是否为完全二叉树
        算法思路:完全二叉树的特点是:没有左子树空而右子树单独存在的情况(前k-1层都是满的,且第k层左边也满)。
        技巧: 按层序遍历方式,先把所有结点(不管当前结       点是否有左右孩子)都入队列.若为完全二叉树, 则层序遍历时得到的肯定是一个连续的不包含空指针的序列.如果序列中出现了空指针,则说明不是完全二叉树。 

用二叉链表法(l_child, r_child)存储包含n个结点的 二叉树,结点的指针区域中会有n+1个空指针。





2 1