二叉树三种遍历的非递归实现(两种后序遍历解法)

递归实现简单,看看就好

//定义二叉树结点
struct BiTreeNode
{
    int data;
    BiTreeNode* left;
    BiTreeNode* right;      
};
//先序
void preOrder(BiTreeNode *root){
    cout<<root->data;
    preOrder(root->left);
    preOder(root->right);
}

//中序
void inOrder(BiTreeNode *root){
    preOrder(root->left);
    cout<<root->data;
    preOder(root->right);
}

//后序
void postOrder(BiTreeNode *root){
    preOrder(root->left);
    preOder(root->right);
    cout<<root->data;
}

非递归实现的话,先序和中序原理是一样的,会重点讲解后序遍历(最难理解的)

//先序
void preOrderS(Node *root)   
{
    stack<Node*> s;
    Node *p=root;
    while(p!=NULL||!s.empty())
    {
        //沿着左支走到底
        //用栈记录走过的路径
        while(p!=NULL)
        {
            cout<<p->data<<" ";
            s.push(p);
            p=p->left;
        }
        
        //当左支走到尽头时,若栈里边还有结点
        //则退栈,后退到跟结点,并且向右支前进
        //此时p!=NULL,会进入以上while循环
        if(!s.empty())
        {
            p=s.top();
            s.pop();
            p=p->right;
        }
    }
}
//注:在 if(!s.empty())中,获取根结点只是为了得到往右支的中转,
//当获得右支指针后,将根结点从栈中弹出,以便返回的时候直接
//回到爷爷结点


//中序
void inOrderS(Node *root){
    stack<Node*> s;
    Node *p=root;
    while(p!=NULL||!s.empty()){
        while(p!=NULL)){
            s.push(p);
            p=p->left;
        }
        if(!s.empty()){
            p=s.top();
            s.pop();
            cout<<p->data;
            p=p->right;
        }
    }
}
//中序遍历和先序遍历的代码几乎一致,除了访问点的位置不一样
//中序遍历是在退栈的时候访问根结点


//后序
void PostOrderS(Node *root) {
    Node *p = root, *r = NULL;
    stack<Node*> s;
    while (p!=NULL || !s.empty()) {
        if (p!=NULL) {//走到最左边
            s.push(p);
            p = p->left;
        }
        else {
            p = s.top();
            if (p->right!=NULL && p->right != r)//右子树存在,未被访问
                p = p->right;
            else {
                s.pop();
                visit(p->val);
                r = p;//记录最近访问过的节点
                p = NULL;//节点访问完后,重置p指针
            }
        }//else
    }//while
}
//因为后序非递归遍历二叉树的顺序是先访问左子树,再访问右子树,最后访问根节点。当用
//堆栈来存储节点,必须分清返回根节点时,是从左子树返回的,还从右子树返回的。所以,
//使用辅助指针r,其指向上一个访问过的节点。如果是从右子树返回的话,右子树的值和r的值是一样的
//也可以在节点中增加一个标志域,记录是否已被访问

另一种解法是使用两个栈。试着在纸上写一下代码。我认为这种解法非常的神奇而优美。你可能觉得这很不可思议,但实际上它做的是反向的先序遍历。亦即遍历的顺序是:节点 -> 右子树 -> 左子树。这生成的是后根遍历的逆序输出。使用第二个栈,再执行一次反向输出即可得到所要的结果。

下面是它的实现步骤:

将根节点压入第一个栈
从第一个栈中弹出一个元素,压入第二个栈
然后分别将该节点的左右孩子压入第一个栈
重复步骤2和步骤3直到第一个栈为空
执行结束,第二个栈中就保存了所有节点的后序遍历输出结果。依次将元素从第二个栈中弹出即可。

void postOrderTraversalIterativeTwoStacks(BinaryTree *root) {
  if (!root) return;
  stack<BinaryTree*> s;
  stack<BinaryTree*> output;
  s.push(root);
  while (!s.empty()) {
    BinaryTree *curr= s.top();
    output.push(curr);
    s.pop();
    if (curr->left)
      s.push(curr->left);
    if (curr->right)
      s.push(curr->right);
  }
  while (!output.empty()) {
    cout << output.top()->data << " ";
    output.pop();
  }
}

你可能感兴趣的:(数据结构与算法,数据结构)