二叉树的遍历:先序中序后序遍历的递归与非递归实现及层序遍历

对于一种数据结构而言,遍历是常见操作。二叉树是一种基本的数据结构,是一种每个节点的儿子数目都不多于2的树。二叉树的节点声明如下:

typedef struct TreeNode *PtrToNode;
typedef struct TreeNode *BinTree;

struct TreeNode
{
    int Data;    //为简单起见,不妨假设树节点的元素为int型
    BinTree Left;
    BinTree Right;
};

二叉树的遍历主要有先序遍历,中序遍历,后序遍历,层序遍历四种方式,下面一一介绍。

1. 先序遍历

  在先序遍历中,对节点的访问工作是在它的左右儿子被访问之前进行的。换言之,先序遍历访问节点的顺序是根节点-左儿子-右儿子。由于树可以通过递归来定义,所以树的常见操作用递归实现常常是方便清晰的。递归实现的代码如下:

void PreOrderTraversal(BinTree BT)
{
    if( BT ) 
    {
        printf(“%d\n”, BT->Data);        //对节点做些访问比如打印         
        PreOrderTraversal(BT->Left);     //访问左儿子
        PreOrderTraversal(BT->Right);    //访问右儿子
    }
}

由递归代码可以看出,该递归为尾递归(尾递归即递归形式在函数末尾或者说在函数即将返回前)。尾递归的递归调用需要用栈存储调用的信息,当数据规模较大时容易越出栈空间。虽然现在大部分的编译器能够自动去除尾递归,但是即使如此,我们不妨自己去除。

非递归先序遍历算法基本思路:使用堆栈

  a. 遇到一个节点,访问它,然后把它压栈,并去遍历它的左子树;

  b. 当左子树遍历结束后,从栈顶弹出该节点并将其指向右儿子,继续a步骤;

  c. 当所有节点访问完即最后访问的树节点为空且栈空时,停止。

  实现代码如下:

void PreOrderTraversal(BinTree BT)
{
    BinTree T = BT;
    Stack S = CreatStack(MAX_SIZE);    //创建并初始化堆栈S
    while(T || !IsEmpty(S))
    {
        while(T)        //一直向左并将沿途节点访问(打印)后压入堆栈 
        {
            printf("%d\n", T->Data);
            Push(S, T);
            T = T->Left;
        }
        if (!IsEmpty(S))
        {
            T = Pop(S);    //节点弹出堆栈
            T = T->Right;  //转向右子树
        }
    }
}

2. 中序遍历

  中序遍历的遍历路径与先序遍历完全一样。其实现的思路也与先序遍历非常相似。其主要的不同点是访问节点顺序不同:中序遍历是访问完所有左儿子后再访问根节点,最后访问右儿子,即为左儿子-根节点-右儿子。

  递归实现的代码如下:

void InOrderTraversal(BinTree BT)
{
    if(BT)
    {
        InOrderTraversal(BT->Left);
        printf("%d\n", BT->Data);
        InOrderTraversal(BT->Right);
    }
}

非递归辅助栈实现代码如下:

void InOrderTraversal(BinTree BT)
{ 
    BinTree T = BT;
    Stack S = CreatStack(MaxSize); //创建并初始化堆栈S
    while(T || !IsEmpty(S))
  {
      while(T)    //一直向左并将沿途节点压入堆栈
      { 
          Push(S,T);
          T = T->Left;
      }
      if(!IsEmpty(S))
      {
          T = Pop(S);                //节点弹出堆栈
          printf("%d\n", T->Data);    //(访问) 打印结点
          T = T->Right;              //转向右子树
      }
  }
}

非递归不用辅助栈实现中序遍历:

     试设计一个非递归算法,按中根顺序遍历非线索二叉树,但不得用任何辅助栈。在执行算法期间,允许改变左孩子指针和右孩子指针的值。

  算法:右线索化+回溯

  •  若当前树的根节点p有左孩子且未被线索化:将其左孩子的最右结点(可为左孩子本身)指向p,即右线索化,然后p = p->lChild;
  •    若p有左孩子但已被线索化,说明该p是回溯上来的,即左孩子已经被访问了,则释放线索化的指针;
  •    若p无左孩子,打印p,向上回溯(即p = p->rChild)。
  • 代码如下(关于二叉树的建立请戳我):
/*
输入:ABDH##I##E##CF#J##G##
*/
#include <cstdio>

typedef struct BTNode* Position;
typedef Position BTree;
typedef char ElementType;
struct BTNode {
    ElementType data;
    Position lChild, rChild;
};

BTree CreateBTree(void);
void Inorder(BTree bt);

int main()
{
    BTree bt = CreateBTree();
    Inorder(bt);
    return 0;
}

void Inorder(BTree bt)
{
    Position p = bt;
    while (p)
    {
        Position pLeft = p->lChild;
        if (pLeft)
        {
            while (pLeft->rChild && pLeft->rChild != p) //找到以p为根结点的树的最右孩子
                pLeft = pLeft->rChild;
            if (pLeft->rChild == NULL)   //线索化
            {
                pLeft->rChild = p;
                p = p->lChild;
                continue;
            }
            else                      //线索化后已被访问
            {
                pLeft->rChild = NULL; //释放指向根节点(祖先)的指针
            }
        }
        printf("%c ", p->data);       //打印
        p = p->rChild;                //向上回溯或者转向右子树
    }
    printf("\n");
}

BTree CreateBTree()                   //按照先序序列建立二叉树
{
    BTree bt = NULL;
    char ch;
    scanf("%c", &ch);
    if (ch != '#')                    //'#'代表空节点
    {
        bt = new BTNode;
        bt->data = ch;
        bt->lChild = CreateBTree();
        bt->rChild = CreateBTree();
    }
    return bt;
}

运行结果:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值