【数据结构】二叉树的三种遍历--(递归+非递归)

前序遍历:根节点—>左子树—>右子树
中序遍历:左子树—>根节点—>右子树
后序遍历:左子树—>右子树—>根节点

【构造一棵树】
我们用数组存树的节点
int a[15] = { 1, 2, ‘#’, 3, ‘#’, ‘#’, 4, 5, ‘#’, 6, ‘#’, 7, ‘#’, ‘#’, 8 }
“#”代表非法值,就是为空的节点

#include<iostream>
#include<stack>
#include<queue>
using namespace std;
template < class T>

struct BinaryTreeNode
{   
    BinaryTreeNode* _left;
    BinaryTreeNode* _right;
    T _data;
    BinaryTreeNode(const T&x)
        :_left(NULL)
        , _right(NULL)
        ,_data(x)
    {}
};

template <class T>
class BinaryTree
{
    typedef BinaryTreeNode<T> Node;

public:
    BinaryTree()
        :_root(NULL)
    {}
    BinaryTree(const T* a, size_t n, const T& invalid)
    {
        size_t index = 0;
        _root = _CreatTree(a, n, invalid, index);//构造树的函数
    }
//构造树
Node* _CreatTree(const T*a, size_t n, const T& invalid, size_t& index)
    {
        Node* root = NULL;
        if (index < n&&a[index] != invalid)
        {
            root = new Node(a[index]);
            root->_left = _CreatTree(a,n,invalid,++index);
            root->_right = _CreatTree(a, n, invalid, ++index);
        }
        return root;
    }
    ~BinaryTree()
    {
        _Destroy(_root);
        cout << endl;
    }
protected:
    Node* _root;

前序——–递归
前序就是遇到根节点就访问,之后访问左子树,再右子树,所以很容易可以写出如下非递归代码

void PrevOrder()
    {
        _PrevOrder(_root);
        cout << endl;
    }
void _PrevOrder(Node* root)
    {
        if (root == NULL)
        {
            return;
        }
        cout << root->_data << " ";
        _PrevOrder(root->_left);
        _PrevOrder(root->_right);
    }

前序——–非递归
这里我们要借用栈,因为栈的特性是后进先出。
先遍历树的最左节点,并把每个节点入栈。在入栈的同时就可以访问根节点。
当cur为NULL时,这颗树的最左边的根节点节点都全部入栈,
然后取出栈顶节点,访问右子树,
这里写图片描述
当栈为空,这个树就遍历结束

void PrevOrder_NonR()//非递归前序遍历
    {
        stack<Node*> s;
        Node* cur = _root;
        while (cur||!s.empty())
        {
            while (cur)
            {
                s.push(cur);
                cout << cur->_data << " ";
                cur = cur->_left;
            }
            Node* top = s.top();
            s.pop();
            cur = top->_right;
        }
        cout << endl;
    }

中序—-递归

void InOrder()//中序
{
    _InOrder(_root);
    cout << endl;
}
    void _InOrder(Node* root)
    {
        if (root == NULL)
        {
            return;
        }

        _InOrder(root->_left);
        cout << root->_data << " ";
        _InOrder(root->_right);
    }

中序—-非递归

依然借助栈,把树的最左边的根节点cur全部入栈,
然后依次取栈顶节点访问,
再访问右子树,右子树不为空的时候继续入栈。

    void InOrder_NonR()//非递归中序遍历
    {
        Node* cur = _root;
        stack<Node*> s;
        while (cur||!s.empty())
        {
            while (cur)
            {
                s.push(cur);
                cur = cur->_left;
            }
            Node* top = s.top();//此时访问的是最左节点
            cout << top->_data << " ";
            s.pop();
            cur = top->_right;
        }
        cout << endl;
    }

后序—-递归

void PosOrder()//后序
{
    _PosOrder(_root);
    cout << endl;
}
void _PosOrder(Node* root)
{
    if (root == NULL)
    {
        return;
    }
    _PosOrder(root->_left);
    _PosOrder(root->_right);
    cout << root->_data << " ";
}

后序—-非递归
还是借助栈。但是此时有一个问题:后序是先访问左子树,在右子树,最后根节点,那么问题来了:–访问右子树,是需要通过先访问根节点,当cur=top->_right,cur不是NULL,把cur入栈,依次访问右子树,最后依次退到根节点访问,但是此时cur=top->_right,cur不等于NULL,再次入栈;可是我们已经把右树访问过了,所以我们会在这里陷入死循环。

那么如何解决呢?
我们可以设置一个标记prev,prev表示上一个访问的节点。当我们取出栈顶节点时,如果他的右子树已经访问过了,就不再把右子树入栈,而直接访问当前节点。

void PostOrder_NonR()//后序非递归
{
    stack<Node*> s;
    Node* cur = _root;
    Node* prev = NULL;
    while (cur||!s.empty())
    {
        while (cur)
        {
            s.push(cur);
            cur = cur->_left;
        }
        Node* top = s.top();

        if (top->_right == NULL || top->_right == prev)
        {
            cout << top->_data << " ";
            prev = top;
            s.pop();
        }
        else
        {
            cur = top->_right;
        }
    }
    cout << endl;
}

猜你喜欢

转载自blog.csdn.net/prefect_boy/article/details/78030084