二叉树的存储方式以及递归和非递归的三种遍历方式

主要过程就是递归调用,也可以用栈来实现。

对于先序遍历来说,蓝色剪头第一次经过的结点,就是遍历的序列,以后再次经历就不算进去了。

复制代码
typedef struct BiNode{
    int data;//数据域
    BiNode *lchild, *rchild;//左右孩子指针
} BiNode, *BiTree;

void preorder(BiNode *root){
    if (root != NULL) {
        //访问根节点
        cout << "先序遍历" << root->data;
        preorder(root->lchild);
        preorder(root->rchild);
    }// end of if
}
复制代码

非递归的先序遍历

根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问其左子树时,再访问它的右子树。因此其处理过程如下:

对于任一结点P:

     1)访问结点P,并将结点P入栈;

     2)判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至1);若不为空,则将P的左孩子置为当前的结点P;

     3)直到P为NULL并且栈为空,则遍历结束。

复制代码
//关键在于何时访问的语句的位置
void preorder(BiTree root){
    //初始化栈
    stack<BiTree> nodes;
    BiNode *p = root;
    while (p != NULL || !nodes.empty()) {
        while (p != NULL) {
            //根左右的顺序遍历
            cout << p->data;
            //进栈
            nodes.push(p);
            //继续移动
            p = p->lchild;
        }
        //p == null
        if (!nodes.empty()) {
            //对 p 重新指向
            p = nodes.top();
            //出栈
            nodes.pop();
            //转到右子树
            p = p->rchild;
        }
    }
}
复制代码

中序遍历、后序遍历和先序遍历思想基本类似,对于中序遍历来说,蓝色剪头第二次经过的结点,就是遍历的序列,之前的和以后的再次经历就不算进序列里去了。对于后序遍历来说,蓝色剪头第三次经过的结点,就是遍历的序列,之前经历的就不算进去了。

LDR左跟右:中序遍历左子树、访问根结点、中序遍历右子树

若二叉树非空

  (1)中序遍历左子树;

  (2)访问根结点;

  (3)中序遍历右子树;

若二叉树为空,结束——基本项(也叫终止项)

若二叉树非空——递归项

    (1)中序遍历左子树;

    (2)访问根结点;

    (3)中序遍历右子树;

中序递归遍历算法

复制代码
void inOrder(BiNode *root){
    if (root != NULL) {
        inOrder(root->lchild);
        cout << "先序遍历" << root->data;
        inOrder(root->rchild);
    }// end of if
}
复制代码

中序的非递归遍历,使用栈

复制代码
//非递归的中序遍历二叉树
void inOrder(BiTree root){
    //非递归中序遍历(左跟右)
    stack<BiTree> nodes;//初始化栈
    //指示指针
    BiNode *p = root;
    //遍历二叉树的循环语句
    while (p != NULL || !nodes.empty()) {
        while (p != NULL) {
            //不为空就入栈
            nodes.push(p);
            //一直向做走,直到为 kong
            p = p->lchild;
        }
        // 需要判断空否,因为需要出栈操作
        if (!nodes.empty()) {
            //令 p 重新指向 栈顶结点
            p = nodes.top();
            //访问根节点(栈顶结点)
            cout << p->data << " ";
            //使用完毕,弹出
            nodes.pop();
            //向右遍历
            p = p->rchild;
        }
    }// end of while
}
复制代码
LRD左右跟:后序遍历左子树、后序遍历右子树、访问根结点

后序遍历序列:BDFGECA

复制代码
//递归后续遍历二叉树
void lastOrder(BiTree root){
    if (root != NULL) {
        lastOrder(root->lchild);
        lastOrder(root->rchild);
        cout << root->data;
    }
}
复制代码

同理有非递归的后续遍历二叉树

在后序遍历中,要保证左孩子和右孩子都已被访问,并且左孩子在右孩子访问之后才能访问根结点。因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。

复制代码
void postOrder3(BiTree root)     //非递归后序遍历
{
    stack<BiTree> nodes;
    //当前结点
    BiNode *cur;
    //前一次访问的结点
    BiNode *pre = NULL;
    //根节点入栈
    nodes.push(root);
    //依次遍历左右子树
    while(!nodes.empty())
    {
        cur = nodes.top();
        //判断 cur 结点的左右孩子子树的情况
        if((cur->lchild == NULL && cur->rchild == NULL) ||
           (pre != NULL && (pre == cur->lchild || pre == cur->rchild)))
        {
            //如果当前结点没有孩子结点或者孩子节点都已被访问过
            cout << cur->data;
            //出栈
            nodes.pop();
            //前一次访问的结点, pre标记已经访问的结点
            pre = cur;
        }
        else
        {
            //左右跟的访问顺序,关键还是访问语句的位置!!!一定是先写右子树,再写左子树,顺序不能错
            //如果当前结点的右子树不为空
            if(cur->rchild != NULL){
                nodes.push(cur->rchild);
            }
            //如果当前结点的左子树不为空
            if(cur->lchild != NULL){
                nodes.push(cur->lchild);
            }
        }
    }
}
复制代码

二叉树遍历的总结:

无论先序、中序、后序遍历二叉树,遍历时的搜索路线是相同的:从根节点出发,逆时针延二叉树外缘移动,对每个节点均途经三次。

先序遍历:第一次经过节点时访问。(ABCD)

中序遍历:第二次经过节点时访问。(BADC)

后序遍历:第三次经过节点时访问。(BDCA)

 

辛苦的劳动,转载请注明出处,谢谢……
http://www.cnblogs.com/kubixuesheng/p/4378266.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值