数据结构学习记录(四)——二叉树的建立及遍历

考研数据结构学习心得记录,代码水平不高,如有错误,我虚心改正!

 

二叉树是数据结构中非常重要的一部分,在学习过程中,我一直对于二叉树的实现存在诸多问题(自以为会做实际是眼高手低……),因此特对二叉树的建立以及前中后序三种遍历方法进行总结和练习,层序遍历等写完队列再写。

先给出二叉树的结构定义:

typedef struct Node
{
    char elem;
    BiTreeBode *lchild,*rchild;
} BiTreeNode,*BiTree;

这个地方略介绍下typedef的用法(作为小白的我之前不太理解为何“多此一举”……)。在这里typedef来命名一个结构时,可以省略该结构的标签(如上可省略Node),且将该结构的指针命名为BiTree,即BiTree等同于Node*。

首先是二叉树的建立。

经过查阅各博主论坛以及各种资料,鉴于考研试题的复习时间紧及代码难度要求略低,这里给出一种简单的二叉树建立方法,二叉树的前序递归建立。

代码如下:

//前序生成二叉树
bool PreCreateBiTree(BiTree &T)
{
    char x;
    cin>>x;
    if(x=='#')
        T=null;
    else
    {
        T=(BiTreeNode*)malloc(sizeof(BiTreeNode));
        if(T)
        {
            T->elem=x;
            PreCreateBiTree(T->lchild);
            PreCreateBiTree(T->rchild);
        }
        else
            cout<<"生成节点错误!"<

这里要求输入前序序列并将空结点输入(以‘#‘代表空结点)。

 

建立二叉树之后,现给出前中后序的递归算法:

//前序递归遍历二叉树并打印
void PreOrder(BiTree &T)
{
    if(T)
    {
        cout<elem<<' ';
        PreOrder(T->lchild);
        PreOrder(T->rchild);
    }
}

//中序递归遍历二叉树并打印
void MidOrder(BiTree &T)
{
    if(T)
    {
        MidOrder(T->lchild);
        cout<elem<<' ';
        MidOrder(T->rchild);
    }
}

//后序递归遍历二叉树并打印
void PostOrder(BiTree &T)
{
    if(T)
    {
        PostOrder(T->lchild);
        PostOrder(T->rchild);
        cout<elem<<' ';
    }
}

递归算法十分简单,但代码性能不好(最重要的是考研试题应该不会考这么水的东西),因此给出三种遍历的非递归算法。

前中序算法的基本思路大致相同,后序算法略有不同,但也差别不大。

对于前中序算法,基本思路是:

  1. 当前结点非空,入栈,进入左子树;
  2. 当前结点为空,出栈,并进入栈顶元素的右子树;
  3. 若栈和当前结点同时为空,退出循环;

而前中序的区别在于访问结点的位置,前序在入栈时访问,中序则在出栈时访问。

 

对于后序算法,在当前结点为空时,不能直接出栈(因为要先访问右孩子才能再访问根),而是判断栈顶元素的右孩子是否存在,若存在右孩子且右孩子未访问过,则进入右子树,否则才执行出栈。

在这里,对于右孩子的检验有两种方法(我见过的只有这两种,且比较简便):

  1. 将二叉树的结点结构改进,多出一个标记位置,记录该结点的右孩子是否被访问过。(王道数据结构)
  2. 设置一个变量,记录上一个被访问的结点(若结点存在右孩子则该结点的前驱一定为右孩子)。

这里我采用第二种方法实现后序遍历

//
//非递归算法

//前序遍历并打印的非递归算法
void PreOrderPro(BiTree &T)
{
    Stack S;                                //声明栈并初始化
    InitStack(S);
    BiTree p=T;                             //建立p指向当前访问的结点
    while(p||!IsEmpty(S))                   //如果p和栈均空则跳出循环
    {
        if(p)                               //结点非空,入栈,打印并进入左孩子
        {
            cout<elem<<' ';
            Push(S,p);
            p=p->lchild;
        }
        else                                //结点为空,出栈并进入栈顶元素的右孩子
        {
            Pop(S,p);
            p=p->rchild;
        }
    }
}

//中序遍历并打印的非递归算法
void MidOrderPro(BiTree &T)
{
    Stack S;                                //声明栈并初始化
    InitStack(S);
    BiTree p=T;                             //建立p指向当前访问的结点
    while(p||!IsEmpty(S))                   //如果p和栈均空则跳出循环
    {
        if(p)                               //结点非空,入栈,并进入左孩子
        {
            Push(S,p);
            p=p->lchild;
        }
        else                                //结点为空,出栈,打印栈顶元素并进入栈顶元素的右孩子
        {
            Pop(S,p);                       //并进入栈顶元素的右孩子
            cout<elem<<' ';
            p=p->rchild;
        }
    }
}


//后序遍历并打印的非递归算法
void PostOrderPro(BiTree &T)
{
    Stack S;                                //声明栈并初始化
    InitStack(S);
    BiTree p=T;                             //建立p指向当前访问的结点
    BiTree mark;                            //mark记录上一个打印过的结点
    while(p||!IsEmpty(S))                   //如果p和栈均空则跳出循环
    {
        if(p)                               //结点非空,入栈,并进入左孩子
        {
            Push(S,p);
            p=p->lchild;
        }
        else
        {
            Top(S,p);                       //结点为空,取栈顶元素
            if(p->rchild!=null&&p->rchild!=mark)//如果栈顶元素右孩子非空
                p=p->rchild;                    //且右孩子不是上一个访问的结点
                                                //则进入右孩子
            else
            {
                cout<elem<<' ';         //若右孩子空或已访问,则将其打印
                mark=p;                     //并将mark置为该结点,出栈并将p置空
                Pop(S,p);
                p=null;
            }
        }
    }
}

 

你可能感兴趣的:(数据结构学习心得,二叉树的遍历,二叉树的建立)