数据结构学习笔记——二叉树的遍历

来源:互联网 发布:21lic单片机项目外包网 编辑:程序博客网 时间:2024/05/20 20:17

二叉树的遍历

顺着某一条搜索路径依次访问二叉树中的结点,使得每个结点均被访问一次,而且仅被访问一次。“访问”的定义有很多,例如:输出结点的信息。
“遍历”是任何类型均有的操作,对线行结构而言,只有一条搜索路径(因为每个结点均只有一个后继),故不需另加讨论。而二叉树是非线性结构,每个结点有两个后继,则存在如何遍历也就是按什么样的搜索路径进行遍历的问题。

对于二叉树而言,可以有三条搜索路径:
1. 先左后右的遍历算法
先(根)序遍历算法
若二叉树为空树,则为空操作。否则,

  • 访问根结点;
  • 先序遍历左子树;
  • 先序遍历右子树。

中(根)序遍历算法
若二叉树为空树,则为空操作。否则,

  • 中序遍历左子树;
  • 访问根结点;
  • 中序遍历右子树。

后(根)序遍历算法
若二叉树为空树,则为空操作。否则,

  • 后序遍历左子树;
  • 后序遍历右子树;
  • 访问根结点。

算法的递归描述

以先序为例

void PreOrderTraverse(BiTree T,Status(*Visit)(TElemType)) { // 初始条件: 二叉树T存在,Visit是对结点操作的应用函数。有改动   // 操作结果: 先序递归遍历T,对每个结点调用函数Visit一次且仅一次   if(T) // T不空   {     Visit(T->data); // 先访问根结点     PreOrderTraverse(T->lchild,Visit); // 再先序遍历左子树     PreOrderTraverse(T->rchild,Visit); // 最后先序遍历右子树   } }

递归算法清晰明了,但缺点是运行效率不高。


中序遍历非递归描述

  1. 由中序遍历过程可知,采用一个栈保存需要返回的结点指针,先扫描根结点的所有左结点并将它们一一进栈。
  2. 然后出栈一个节点,显然该结点是没有左孩子结点或者左孩子结点已访问过,则访问它。
  3. 然后扫描该结点的右孩子结点,将其进栈,再扫描该右孩子结点的所有左结点并一一进栈,如此这样,直到栈空为止。
Status InOrderTraverse1(BiTree T,Status(*Visit)(TElemType)) { // 采用二叉链表存储结构,Visit是对数据元素操作的应用函数。   // 中序遍历二叉树T的非递归算法(利用栈),对每个数据元素调用函数Visit   SqStack S;   InitStack(S);   while(T||!StackEmpty(S))   {     if(T)     { // 根指针进栈,遍历左子树       Push(S,T);       T=T->lchild;     }     else     { // 根指针退栈,访问根结点,遍历右子树       Pop(S,T);       if(!Visit(T->data))         return ERROR;       T=T->rchild;     }   }   printf("\n");   return OK; }

先序遍历的非递归算法

由先序遍历过程可知,先访问根结点再访问左子树,最后访问右子树。因此,先将根结点进栈,在栈不空时循环:出栈p,访问*p结点,将其左孩子结点进栈,再将左孩子结点进栈。


后序遍历的非递归算法


  1. 先扫描根结点的所有左结点并将它们一一进栈,出栈一个结点即为当前结点,然后扫描该结点的右孩子结点并进栈,再扫描右孩子结点的所有左结点并进栈。
  2. 当一个结点的左、右孩子结点均被访问后再访问该结点,如此这样,直到栈空为止。

后序遍历非递归算法中的难点是如何判断一个结点的右孩子结点已被访问过,为此用p保存刚刚访问过的结点(初值为NULL),若b->rchild==p成立(在后序遍历中,*b的右孩子结点一定刚好在*b之前访问),说明*b的左、右子树均已被访问,现在应访问*b。

0 0
原创粉丝点击