利用栈实现线索化二叉树_6.3 二叉树的遍历与线索化(3)

b8e17c30127073f4478aaebe6eb14560.png

返回目录:

Chilan Yu:《数据结构》目录链接​zhuanlan.zhihu.com
9bcc8db52ef4c88443d5af1b2791d4d5.png

6.3.3 基于栈的递归消除

在大量复杂的情况下,递归的问题无法直接转换成循环,需要采用工作栈消除递归。

应用递归进层三件事与递归退层三件事的原则

算法思想】:

递归进层时,需保留信息:

  • (1)保存本层参数,返回地址
  • (2)传递参数,分配局部数据空间
  • (3)控制转移

递归退层时,需恢复信息:

  • (1)恢复上层
  • (2)传递结果
  • (3)转断点执行

1. 先序遍历二叉树的非递归算法

先贴出先序遍历二叉树的递归算法:

/*先序遍历二叉树(根左右)*/
void PreOrder(BiTree root){//先序遍历二叉树,root为指向二叉树(或某一子树)根结点的指针
    if(root!=NULL){//如果root不为空
        Visit(root->data);//访问根结点
        PreOrder(root->LChild);//先序遍历左子树
        PreOrder(root->RChild);//先序遍历右子树
    }
}

对比给出先序遍历二叉树的非递归算法:

/*先序遍历二叉树的非递归算法(调用栈操作的函数)*/
void PreOrder(BiTree root){//先序遍历二叉树的非递归算法
    SeqStack S;//建栈
    InitStack(&S);//初始化栈
    BiTree p = root;//建立一个p指针拷贝root指针
    while(p!=NULL || !IsEmpty(&S)){//当p不为空,或者栈S不为空时进入循环
        if(p!=NULL){//如果p不为空指针,根指针进栈,访问根结点,遍历左子树
            Push(&S,p);
            printf("%c",p->data);
            p = p->LChild;
        }
        else{//如果p为空指针,根指针退栈,遍历右子树
            Pop(&S,&p);
            p = p->RChild;
        }
    }
}

2. 中序遍历二叉树的非递归算法

先贴出中序遍历二叉树的递归算法:

/*中序遍历二叉树(左根右)*/
void InOrder(BiTree root){//中序遍历二叉树,root为指向二叉树(或某一子树)根结点的指针
    if(root!=NULL){//如果root不为空
        InOrder(root->LChild);//中序遍历左子树
        Visit(root->data);//访问根结点
        InOrder(root->RChild);//中序遍历右子树
    }
}

对比给出中序遍历二叉树的非递归算法:

/*中序遍历二叉树的非递归算法(调用栈操作的函数)*/
void InOrder(BiTree root){//中序遍历二叉树的非递归算法
    SeqStack S;//建栈
    InitStack(&S);//初始化栈
    BiTree p = root;//建立一个p指针拷贝root指针
    while(p!=NULL || !IsEmpty(&S)){//当p不为空,或者栈S不为空时进入循环
        if(p!=NULL){//如果p不为空指针,根指针进栈,遍历左子树
            Push(&S,p);
            p = p->LChild;
        }
        else{//如果p为空指针,根指针退栈,访问根结点,遍历右子树
            Pop(&S,&p);
            printf("%c",p->data);
            p = p->RChild;
        }
    }
}

3. 后序遍历二叉树的非递归算法

先贴出后序遍历二叉树的递归算法:

/*后序遍历二叉树(左根右)*/
void PostOrder(BiTree root){//后序遍历二叉树,root为指向二叉树(或某一子树)根结点的指针
    if(root!=NULL){//如果root不为空
        PostOrder(root->LChild);//后序遍历左子树
        PostOrder(root->RChild);//后序遍历右子树
        Visit(root->data);//访问根结点
    }
}

后序遍历的非递归算法比较复杂。由于后序遍历是LRD,要求左、右子树都访问完后,最后访问根结点。如何判断当前栈顶结点的左、右子树都已访问过?

这里采用的方法是:判断刚访问过的结点q是不是当前栈顶结点p的右孩子。

判别是非应该访问当前栈顶结点p时,有以下两种情况

  • (1)p无右孩子,此时应该访问根结点
  • (2)p的右孩子是刚被访问过的结点q(表明p的右子树已遍历过),此时也应该访问根结点。

除这两种情况外,均不应访问根,而是要继续进入右子树中。

因此,算法采用了记录刚访问结点的方法,以便在遍历过程中利用前驱q与当前结点p的关系做判别。

算法思想】:

从根结点开始,只要当前结点存在,或者栈不空,则重复下面操作:

  • (1)从当前结点开始,进栈并遍历左子树,直到左子树为空
  • (2)如果栈顶结点的右子树为空,或者栈顶结点的右孩子为刚访问过的结点,则退栈并访问,然后将当前结点指针置为空
  • (3)否则,遍历右子树
/*后序遍历二叉树的非递归算法(调用栈操作的函数)*/
void PostOrder(BiTree root){//后序遍历二叉树的非递归算法
    BiTree p,q;//建立两个指针
    SeqStack S;//建栈
    q = NULL;//q指针置为空,用来记录刚访问过的结点
    p = root;//p指针拷贝root指针,用来记录当前正在访问的结点
    InitStack(&S);//初始化栈
    while(p!=NULL || !IsEmpty(&S)){//只要当前结点存在,或者栈不空,则重复下面操作
        if(p!=NULL){//从当前结点开始,进栈并遍历左子树,直到左子树为空
            Push(&S,p);
            p = p->LChild;
        }
        else{//当左子树为空时
            GetTop(&S,&p);//用p来存储栈顶结点
            if((p->RChild==NULL) || (p->RChild==q)){//栈顶结点的右子树为空,或者栈顶结点的右孩子为刚访问过的结点
                printf("%c",p->data);//输出当前栈顶结点
                q = p;//准备进行下一步,所以把q置为p
                Pop(&S,&p);//退栈
                p = NULL;//然后将当前结点指针置为空
            }
            else p = p->RChild;//否则,遍历右子树
        }
    }
}

ADDITION:

以上函数所需辅助的栈+建树函数:

typedef BiTree StackElementType;
#define Stack_Size 50//设栈中元素个数为50

/*顺序栈的存储结构*/
typedef struct{
    StackElementType elem[Stack_Size];//用来存放栈中元素的一维数组
    int top;//用来存放栈顶元素的下标,top为-1表示空栈
}SeqStack;
/*初始化顺序栈*/
void InitStack(SeqStack * S){//构造一个空栈S
    S->top = -1;
}
/*顺序栈进栈运算*/
int Push(SeqStack * S,StackElementType x){//将x置入S栈新栈顶
    if(S->top==Stack_Size-1) return 0;//栈已满
    S->top++;//修改栈顶指针
    S->elem[S->top] = x;//x进栈
    return 1;
}
/*顺序栈出栈运算*/
int Pop(SeqStack * S,StackElementType * x){//将S栈顶元素弹出,放到x所指的存储空间中带出
    if(S->top==-1) return 0;//栈为空
    *x = S->elem[S->top];//栈顶元素赋给x
    S->top--;//修改栈顶指针
    return 1;
}
/*顺序栈读栈顶元素运算*/
int GetTop(SeqStack * S,StackElementType * x){//将S栈顶元素读出,放到x所指的存储空间中,栈顶指针保持不变
    if(S->top==-1) return 0;//栈为空
    *x = S->elem[S->top];//栈顶元素赋给x
    return 1;
}
/*顺序栈判空函数*/
int IsEmpty(SeqStack * S){//若S为空栈,则函数返回1,否则返回0
    return (S->top==-1 ? 1 : 0);
}

/*****************************************************/

typedef char DataType;
/*二叉树的二叉链表结点结构定义*/
typedef struct Node{//结点结构
    DataType data;//结点数据
    struct Node * LChild;//左孩子指针
    struct Node * RChild;//右孩子指针
}BiTNode, * BiTree;
/*用扩展先序遍历序列创建二叉链表*/
void CreateBiTree(BiTree * bt){//这边的bt是指针的指针
    char ch;
    ch = getchar();
    if(ch=='#') *bt = NULL;
    else{
        *bt = (BiTree)malloc(sizeof(BiTNode));//用(*bt)指针开辟结点空间
        (*bt)->data = ch;
        CreateBiTree( &( (*bt)->LChild  ) );
        CreateBiTree( &( (*bt)->RChild  ) );
    }
}

调用程序的主函数:

int main()
{
    BiTree T;//开辟一个指向树的T指针
    CreateBiTree(&T);//传址建树
    PreOrder(T);//先序遍历二叉树的非递归算法(调用栈操作的函数)
    printf("n");
    InOrder(T);//中序遍历二叉树的非递归算法(调用栈操作的函数)
    printf("n");
    PostOrder(T);//后序遍历二叉树的非递归算法(调用栈操作的函数)
    return 0;
}

d5caf3a9716ff3cc462476ff772bf2ef.png

返回目录:

Chilan Yu:《数据结构》目录链接​zhuanlan.zhihu.com
9bcc8db52ef4c88443d5af1b2791d4d5.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值