二叉树的遍历--前,中,后

本文详细介绍了二叉树的储存结构,并分别探讨了前序、中序和后序遍历的递归和非递归版本,强调了递归可能导致的栈溢出问题以及非递归方法的优势。
摘要由CSDN通过智能技术生成

目录

二叉树的储存结构(定义)

(一)前序遍历

(1)递归版本

(2)非递归版本

(二)中序遍历

(1)递归版本

(2)非递归版本

(三)后序遍历

(1)递归版本

(2)非递归版本

(四)总结

前序遍历(Preorder Traversal)

中序遍历(Inorder Traversal)

后序遍历(Postorder Traversal)

递归与非递归


二叉树的储存结构(定义)

struct TreeNode{
int val;  //节点储存数据(可以是整数,字符,字符串)
TreeNode *left;  //指向左节点的指针
TreeNode *right;  //指向右节点的指针
TreeNode(int x):val(x),left(NULL),right(NULL){};  //构造函数
};

(一)前序遍历

(1)递归版本

思路:

若二叉树为空,什么都不做,否则:

  1. 先访问根节点
  2. 再前序遍历左子树
  3. 最后前序遍历右子树

算法实现:

void PreOrder(TreeNode T){
if(T!=NULL){
 visit(T);  //访问结点
 PreOrder(T->lelt);  //遍历节点左子树
 PreOrder(T->right);  //遍历节点右子树
 }
}
void visit(TreeNode T){
 print("树节点的值:%d",T->data);
}

理解递归可以参照栈的定义

(2)非递归版本

前序遍历的非递归算法,就是将上面递归函数隐式调用栈的过程给显示表示出来,即利用一个辅助栈,来进行访问结点并入栈遍历左子树,结点出栈遍历右子树。

  1. 首先检查根节点是否为空
  2. 使用一个stack栈来辅助遍历。初始时,将根节点压入栈中
  3. 当左节点不为空时,压入栈中
  4. 当节点为空而栈不为空时,栈顶元素出栈,遍历栈顶元素右子树
  5. 节点为空并且栈为空,遍历结束

实现代码

void PreOrder(TreeNode T){
SqStack S;  //申请一个辅助栈
InitStack(&S);  //初始化
TreeNode p=T;  //p为遍历指针
while(p||!IsEmpty(S))  //当p不为空或栈不为空时循环
{
  if(p){  //一路向左
   visit(p);  //访问当前节点并入栈
   Push(&S,p);
   p=p->left;  //左孩子不为空,一直向左走
 }else  //出栈并转向栈节点的右子树
  {
  Pop(&S,p);  //栈顶元素出栈
  p=p->right;  //向右子树走,p赋值为当前节点的右节点
  }
 }  //返回while循环继续if-else语句
}

(二)中序遍历

(1)递归版本

思路:

二叉树为空,什么也不做,否则:

  1. 首先遍历左子树
  2. 然后访问根节点(通常是打印节点的值或执行某种操作)
  3. 最后遍历右子树

实现代码

void InOrder(TreeNode T){
if(T!=NULL)  //当树不为空时
 {
  InOrder(T->left);  //遍历左子树
  visit(T);  //访问根节点
  InOder(T->right);  //遍历右子树
 }
}
void visit(TreeNode T)
{ 
  printf("根节点的值:%d\n",T->data);
}

(2)非递归版本

思路:

  1. 二叉树为空,啥也不做
  2. 节点不为空,入栈,遍历左子树
  3. 节点为空但栈不为空,出栈,遍历出栈元素的右子树
  4. 节点为空且栈也为空,遍历结束

算法实现

void InOrder(TreeNode T)
{
  SqStack S;  //申请一个栈
  InitStack(&S);  //初始化
  TreeNode p=T;  //p为遍历指针
  while(p||!IsEmpty(S))  //当p不为空或栈不为空时循环
    {if(p)  //一路向左
      {Push(&S,p);  //当前节点入栈
        p=p->left;  //左孩子不为空,一直向左走
      }else  //出栈,并转向当前节点的右子树
        {
        Pop(&S,p);  //顶栈元素出栈
        visit(p);  //访问出栈节点
        p=p->right;  //右孩子不为空,一直向右走
        }
     }  //返回while循环执行if-else语句

(三)后序遍历

(1)递归版本

思路:

若二叉树为空,什么也不用左,否则:

  1. 后序遍历左子树
  2. 后序遍历右子树
  3. 访问根节点

实现代码

void PostOrder(TreeNode T){
 if(T!=NULL)  //当二叉树不为空时
  {T=T->left;  //遍历节点左子树
   T=T->right;  //遍历节点右子树
   visit(T);  //访问节点
   }
}
void visit(T){
 printf("当前节点的值为:%c",T->data);
}

(2)非递归版本

后序遍历非递归算法最需要解决的问题就是——上面的两次出栈问题(即判别哪次出栈是访问右子树,哪次出栈是访问自身),这是其利用辅助栈需要解决的事,而解决此事也有很多方法,这里介绍标志法,即设立一个标志来判别出栈。

思路

  1. 当二叉树为空时,什么都不用做
  2. 当节点不为空时,入栈并设立标志tag=0,遍历当前节点的左子树
  3. 当节点为空时,需判断栈是否为空,为空则遍历结束;不为空则有以下两种情况:
  • 当tag=0时(说明栈顶元素还没有遍历右子树),则重新标志tag=1(此时还在栈中),并遍历右子树
  • 当tag=1时(说明栈顶元素左右子树都已遍历),出栈访问栈顶元素

实现代码

struct stack{
 TreeNode t;  //指向二叉树的遍历指针
 int tag=0;  //设置标志
}  //tag=0时,只有左子树都已遍历;tag=1时,左右子树都已遍历

void PostOrder(TreeNode T)
{
  struct stack s[Maxsize];  //定义一个stack类型的数组作为栈空间,大小为maxsize
  int top=-1;  //初始化一个栈顶指针
  while(T!=NULL||top>=0)   //当二叉树不为空或栈不为空时,执行循环
   {
     s[++top]->t=T;  //将节点压入栈中
     s[++top]->tag=0;  //设置tag=0,表明右子树还未遍历
     T=T->left;  //沿左子树一路向下
    }
  while(top!=-1&&s[top]->tag==1)  //当无法继续向左遍历时(即T=NULL),且当前节点的左右子树都已遍历
    {
      visit(s[top--]->t);  //弹出栈顶元素
       if(top!=-1)
        {
          s[top]->tag=1;  //设置tag=1,表面开始访问右子树
          T=s[top].t->right;  //将 T 更新为栈顶节点的右子节点
         }   //继续向下遍历右子树
     }
}

(四)总结

二叉树的前序、中序和后序遍历是三种基本的二叉树遍历方法,每种方法访问节点的顺序不同。下面是对这三种遍历方法的总结:

前序遍历(Preorder Traversal)

前序遍历的顺序是:根节点 -> 左子树 -> 右子树。

通常用于打印结构化的输出,如打印目录结构。

中序遍历(Inorder Traversal)

中序遍历的顺序是:左子树 -> 根节点 -> 右子树。

对于二叉搜索树,中序遍历可以按顺序访问所有节点。

后序遍历(Postorder Traversal)

后序遍历的顺序是:左子树 -> 右子树 -> 根节点。

常用于删除二叉树或者释放资源,因为在释放节点前已经处理了子节点。

递归与非递归

递归方法简单直观,但可能会导致栈溢出(尤其是对于深度很大的二叉树)。非递归方法使用栈来模拟递归过程,避免了栈溢出的风险,但代码相对复杂。

有错误请大家指出呀~

有帮助请点赞收藏呀~

​​​​​​​

  • 26
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值