二叉树的前序、中序、后续遍历及非递归实现代码(C++实现)

树的结构定义

每一个树节点包括自己存储的数据和左右孩子的地址。
struct treenode {
    int data;  //默认存储int类型,需要更改模板类
    treenode* left;
    treenode* right;
    treenode() :data(0), left(nullptr), right(nullptr) {}
    treenode(int data) :data(data), left(nullptr), right(nullptr) {}
};

class tree
{
public:
    int end;//自定义的叶子节点的左右孩子结束值,简化输入
    treenode* p;
    tree(int data) :p(new treenode()), end(data) {}
    tree():p() {}
    void creat(treenode*& p);//创建一颗树
    void preorder(treenode* p);
    void postorder_stack(treenode* p, std::stack<treenode*>s);
    void preorder_stack(treenode* p, std::stack<treenode*>s);
};
使用递归方法创建树

void tree::creat(treenode*& p) {
    int data;
    std::cin >> data;
    if (data == this->end) {
        delete p;
        p = nullptr;
    }
    else {
        p = new treenode(data);
        creat(p->left);
        creat(p->right);
    }

}

递归遍历

使用递归方法遍历二叉树,输出节点,只在于输出节点的位置
//递归方式遍历
void tree::preorder(treenode* p) {
    if (p == nullptr)
        std::cout;
    else {
        //std::cout << p->data  写在此处为先序, 即每次读取一个节点就访问这个节点
        preorder(p->left);
        //std::cout << p->data 写在此处为中序, 即每次左子树访问完成访问节点
        preorder(p->right);
        std::cout << p->data << " ";//此处为后序, 每次右子树访问完全后访问节点
    }
}

使用栈实现遍历

使用栈的方式遍历,理论上所有递归函数都可以用栈方式重写,因为递归实质就是函数的不断进出栈过程。

栈实现先序和中序比较简单,因为访问方式符合栈的进出方式,左子树不断进栈,无左子树,当前出栈,得到父亲,再遍历
右子树。

先序和中序

void tree::preorder_stack(treenode* p, std::stack<treenode*> s)
{
    treenode* p1 = p;
    while (p1 != nullptr || !s.empty()) {
        if (p1 != nullptr) {
            s.push(p1);
            p1 = p1->left;//遍历左子树,如果有当前节点存在,就压进栈
           //std::cout << p1->right;
        }
        else {
            p1 = s.top();//左子树为空,回到当前栈顶节点,也就是不存在节点的父亲,准备遍历右子树
            s.pop();//当前节点出栈
            std::cout << p1->data << " ";//访问当前节点
            p1 = p1->right;//遍历右子树
        }
    }
    std::cout << std::endl;
}
进行栈实现的后续遍历会复杂很多,我们需要使用一个零时的节点去记录是否访问过每个节点左右孩子,如果访问过了
接需要标记出来,我使用的是如果左孩子被访问了,就将左孩子干掉,如果右孩子被访问了,就把右孩子干掉,
但更好的方法是创建一个记录值,访问过左孩子,数值+1,数值为1只能访问右孩子,数值再+1,为2则不能访问。

后续遍历(较难)

void tree::postorder_stack(treenode* p, std::stack<treenode*>s)
{
    treenode* p1 = p;
    treenode* temp;
    s.push(p1);
    while (!s.empty()) {
        if (p1->left !=  nullptr) {//如果左子树不为空,遍历左子树
            s.push(p1->left);//不为空就压栈
            temp = p1;//temp记录当前节点
            p1 = p1->left;//p1指向下一节点
            temp->left = nullptr;//temp记录节点左孩子为空
        }
        else {
            if (p1->right != nullptr) {//每一次没有左孩子就来遍历右孩子
                temp = p1;//记录当前节点
                p1 = p1->right;//p1指向下一节点
                temp->right = nullptr;//temp记录节点右孩子为空,标记为已访问过
                s.push(p1);//p1压入栈
            }
            else {//如果没左孩子也没右孩子了,就输出当前节点
                std::cout << p1->data << " ";
                s.pop();//完成当前节点访问,出栈
                if(!s.empty())
                p1 = s.top();//如果栈不为空,获取栈顶元素
            }
        }
    }
    std::cout << std::endl;
}
栈实现的后续遍历是我根据递归时出入栈的方式模拟过程实现的,建议大家在学习的时候多根据原理反推实现,会有

很好的效果!

                                                                                                    PS:2020.11.25
                                                                                                        BY:Aaa2   
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值