二叉树的非递归遍历:先根遍历、中根遍历、后根遍历

二叉树的非递归遍历

  • 二叉树的三种遍历方式也可以通过非递归的方法借助栈来实现。
  • 通过控制节点的出栈和入栈先后顺序来实现对树的不同方式的遍历。

非递归先根遍历二叉树

  • 当栈不为空或者当前节点为不为空,执行操作:
  • 从根节点开始,依次访问树中最左端的节点并入栈,当节点为空停止入栈。
  • 取栈顶元素为当前节点并出栈,如果当前节点有右子树,则遍历其右子树。
void PreorderPrint(SearchBTree T)       // 先序根(序)遍历二叉树并打印出结点值
{
    if (T == nullptr)
    {
        cout << "Empty tree!";
        exit(1);
    }
    Stack S = new StackNode;        // 借助栈来实现
    initStack(S);

    TreeNode* pT = T;       // 创建临时结点指向根节点

    while (pT || !isEmpty(S))           // 当结点不为空或者栈不为空执行循环
    {
        if (pT)                         // 当pT不为空
        {
            Push(S, pT);                // pT入栈
            cout << pT->data << " ";    // 打印当前结点
            pT = pT->left;              // pT移动指向其左孩子
        }
        else
        {       // pT为空,则
            pT = getTop(S);
            Pop(S);                  // 若pT为空表示左子树的左孩子全部遍历完,依次出栈
            pT = pT->right;             // 当左孩子及根结点遍历完之后,开始遍历其右子树
        }
    }
    cout << endl;
    delete S;
}

非递归中根遍历二叉树

  • 当栈不为空或者当前节点为不为空,执行操作:
  • 同样地,先依次将根节点及其左子树最左端的节点入栈,但不进行访问。当节点为空,则停止入栈。
  • 访问栈顶元素作为当前节点并出栈,如果当前节点有右子树,则遍历访问其右子树。
void InorderPrint(SearchBTree T)        // 中根(序)遍历二叉树并打印出结点值
{
    if (T == nullptr)
    {
        cout << "Empty tree!";
        exit(1);
    }
    Stack S = new StackNode;        // 借助栈来实现
    initStack(S);

    TreeNode* pT = T;       // 创建临时结点指向根节点

    while (pT || !isEmpty(S))           // 当结点不为空或者栈不为空执行循环
    {
        if (pT)                         // 当pT不为空
        {
            Push(S, pT);                // pT入栈
            pT = pT->left;              // pT移动指向其左孩子
        }
        else
        {       // pT为空,则
            pT = getTop(S);
            Pop(S); cout << pT->data << " ";    // 若pT为空表示左子树的左孩子全部遍历完,依次出栈并打印
            pT = pT->right;             // 当左孩子及根结点遍历完之后,开始遍历其右子树
        }
    }
    cout << endl;
    delete S;
}

非递归后根遍历二叉树

  • 非递归后根遍历相比前两个有点麻烦,需要引入一个中间变量标记已经访问节点。
  • 当栈不为空或者当前节点为不为空,执行操作:
  • 依次将根节点及其左子树的左端节点入栈,但不进行访问,当节点为空,停止入栈。
  • 取栈顶元素作为当前节点,如果当前节点的右孩子(右子树)不为空且其右孩子不是上一次访问的节点。则当前节点变为其右子树,遍历其右子树。如果当前节点的右孩子为空或者其右孩子已经被访问,则访问当前节点,标记当前节点为已访问节点,出栈。将当前节点置为空(此时右孩子访问过了),继续取栈顶元素(为了访问根节点)。
void PostorderPrint(SearchBTree T)      // 先根(序)遍历二叉树并打印出结点值
{
    if (T == nullptr)
    {
        cout << "Empty tree!";
        exit(1);
    }
    Stack S = new StackNode;        // 借助栈来实现
    initStack(S);

    TreeNode* pT = T;       // 创建临时结点指向根节点
    TreeNode* qT = nullptr;

    while (pT || !isEmpty(S))           // 当结点不为空或者栈不为空执行循环
    {
        if (pT)                         // 当pT不为空
        {
            Push(S, pT);                // pT入栈
            pT = pT->left;              // pT移动指向其左孩子
        }
        else
        {
            pT = getTop(S);         // 取栈顶元素作为当前结点
            if (pT->right && pT->right != qT)   // 若当前节点有右孩子且不是上一次已经被访问的结点
            {
                pT = pT->right;     // 指向其右孩子
            }
            else
            {                       // 若当前结点没有右孩子或者未被访问,则
                Pop(S);             // 出栈
                cout << pT->data << " ";    // 访问当前结点的数据
                qT = pT;                    // 令pT记录当前结点,用于稍后判断是否已经被访问过
                pT = nullptr;               // 将当前结点赋值为空
            }
                // 当左孩子及根结点遍历完之后,开始遍历其右子树
        }
    }
    cout << endl;
    delete S;
}

附总的代码实现

#include<iostream>
using namespace std;
typedef int ElemType;

typedef struct _TreeNode {  // 定义二叉查找树的结构
    ElemType data;          // 数据
    struct _TreeNode* left;         // 指向左孩子指针
    struct _TreeNode* right;        // 指向其右孩子指针 
}TreeNode, *SearchBTree;

typedef SearchBTree StackElemType;
typedef struct StackNode {          // 定义栈的结构(基于链表)
    SearchBTree data;
    StackNode *next;
}*Stack;

void initStack(Stack St)        // 初始化栈,把头节点看作是栈顶
{
    St->next = nullptr;
}

int isEmpty(Stack St)
{
    return St->next == nullptr;
}

void Push(Stack St, SearchBTree x)      // 每次从将元素插入到头节点的位置,头节点上移
{
    StackNode* q = new StackNode;
    q->data = x;
    q->next = St->next;     // q的next域指向St(头节点)的后继结点
    St->next = q;           // St(头节点/栈顶)next域指向q,实现栈往上移
}

void Pop(Stack St)      // 出栈
{
    StackNode* p = St->next;        // 临时结点指向头结点(栈顶)的后继结点
    St->next = p->next;     // 栈顶下移
    delete p;           // 释放空间
}

SearchBTree getTop(Stack St)
{
    return St->next->data;
}

SearchBTree EmptyTree(SearchBTree T)    // 初始化、构造一颗空树/销毁一颗树
{
    if (!T)
    {
        EmptyTree(T->left);     // 递归地释放空间
        EmptyTree(T->right);
        delete T;
    }
    return nullptr;
}

void Insert(SearchBTree &T, ElemType x)
{
    if (!T)
    {
        TreeNode* pT = new TreeNode;        // 申请节点空间
        pT->data = x;                       // 为节点赋值
        pT->left = pT->right = nullptr;
        T = pT;                             // 将pT赋给T
    }
    else
    {
        if (x < T->data)                // 如果x小于某个结点的数据
            Insert(T->left, x);         // 递归地在其左子树上寻找空结点插入
        else if (x > T->data)           // 如果x大于某个结点的数据
            Insert(T->right, x);        // 递归地在其左子树上寻找空结点插入
    }
}

void PreorderPrint(SearchBTree T)       // 先序根(序)遍历二叉树并打印出结点值
{
    if (T == nullptr)
    {
        cout << "Empty tree!";
        exit(1);
    }
    Stack S = new StackNode;        // 借助栈来实现
    initStack(S);

    TreeNode* pT = T;       // 创建临时结点指向根节点

    while (pT || !isEmpty(S))           // 当结点不为空或者栈不为空执行循环
    {
        if (pT)                         // 当pT不为空
        {
            Push(S, pT);                // pT入栈
            cout << pT->data << " ";    // 打印当前结点
            pT = pT->left;              // pT移动指向其左孩子
        }
        else
        {       // pT为空,则
            pT = getTop(S);
            Pop(S);                  // 若pT为空表示左子树的左孩子全部遍历完,依次出栈
            pT = pT->right;             // 当左孩子及根结点遍历完之后,开始遍历其右子树
        }
    }
    cout << endl;
    delete S;
}

void InorderPrint(SearchBTree T)        // 中根(序)遍历二叉树并打印出结点值
{
    if (T == nullptr)
    {
        cout << "Empty tree!";
        exit(1);
    }
    Stack S = new StackNode;        // 借助栈来实现
    initStack(S);

    TreeNode* pT = T;       // 创建临时结点指向根节点

    while (pT || !isEmpty(S))           // 当结点不为空或者栈不为空执行循环
    {
        if (pT)                         // 当pT不为空
        {
            Push(S, pT);                // pT入栈
            pT = pT->left;              // pT移动指向其左孩子
        }
        else
        {       // pT为空,则
            pT = getTop(S);
            Pop(S); cout << pT->data << " ";    // 若pT为空表示左子树的左孩子全部遍历完,依次出栈并打印
            pT = pT->right;             // 当左孩子及根结点遍历完之后,开始遍历其右子树
        }
    }
    cout << endl;
    delete S;
}

void PostorderPrint(SearchBTree T)      // 先根(序)遍历二叉树并打印出结点值
{
    if (T == nullptr)
    {
        cout << "Empty tree!";
        exit(1);
    }
    Stack S = new StackNode;        // 借助栈来实现
    initStack(S);

    TreeNode* pT = T;       // 创建临时结点指向根节点
    TreeNode* qT = nullptr;

    while (pT || !isEmpty(S))           // 当结点不为空或者栈不为空执行循环
    {
        if (pT)                         // 当pT不为空
        {
            Push(S, pT);                // pT入栈
            pT = pT->left;              // pT移动指向其左孩子
        }
        else
        {
            pT = getTop(S);         // 取栈顶元素作为当前结点
            if (pT->right && pT->right != qT)   // 若当前节点有右孩子且不是上一次已经被访问的结点
            {
                pT = pT->right;     // 指向其右孩子
            }
            else
            {                       // 若当前结点没有右孩子或者未被访问,则
                Pop(S);             // 出栈
                cout << pT->data << " ";    // 访问当前结点的数据
                qT = pT;                    // 令pT记录当前结点,用于稍后判断是否已经被访问过
                pT = nullptr;               // 将当前结点赋值为空
            }
                // 当左孩子及根结点遍历完之后,开始遍历其右子树
        }
    }
    cout << endl;
    delete S;
}

int main()
{
    const ElemType rawdata[] = { 19, 7, 9, 15, 23, 39, 4, 2, 75, 100, 43, 58 };
    SearchBTree myTree = new TreeNode;
    myTree = EmptyTree(myTree);     // 初始化树
    for (int i = 0;i < sizeof(rawdata) / sizeof(ElemType);i++)
    {
        Insert(myTree, rawdata[i]);     // 向树中插入给定数据
    }

    cout << "The inorder print of the tree is: \n";
    InorderPrint(myTree);
    cout << "The preorder print of the tree is: \n";
    PreorderPrint(myTree);
    cout << "The postorder print of the tree is: \n";
    PostorderPrint(myTree);

    delete myTree;
    system("pause");
    return 0;
}
  • 操作运行结果
The preorder print of the tree is:
19 7 4 2 9 15 23 39 75 43 58 100
The inorder print of the tree is:
2 4 7 9 15 19 23 39 43 58 75 100
The postorder print of the tree is:
2 4 15 9 7 58 43 100 75 39 23 19

猜你喜欢

转载自blog.csdn.net/weixin_40170902/article/details/80732947