zoukankan      html  css  js  c++  java
  • 二叉树的遍历(基于栈的非递归方式实现)

    在写二叉树的时候如果用递归实现二叉树的遍历很简单,但是用非递归来实现二叉树的遍历就不那么简单了需要一些技巧。

    那为什么还要非递归实现呢?个人理解:如果树的高度很大,超过了允许递归的次数,那么就会出错,比如我记得python只允许递归100次(不知道记错没)

    这时候用迭代就要保险的多,不会出错。

    下面先来做基本的准备说明:

     1 #include<iostream>
     2 #include<stack>
     3 
     4 #define null NULL
     5 
     6 template<typename Entry>
     7 class binary_node
     8 {
     9     public:
    10         friend class binary_tree;
    11         Entry data;
    12         binary_node* lchild;
    13         binary_node* rchild;
    14         binary_node():lchild(null),rchild(null){}
    15         ~binary_node(){}
    16 };
    17 
    18 template<typename Entry>
    19 class binary_tree
    20 {
    21     binary_node* root;
    22     
    23     public:
    24         binary_tree():root(null){}
    25         ~binary_tree(){}
    26         void preordertraversal(binary_node*,void (*visit)(Entry& bt));
    27         void ineordertraversal(binary_node*,void (*visit)(Entry& bt));
    28         void posteordertraversal(binary_node*,void (*visit)(Entry& bt));
    29         
    30 };

    先来看看前序遍历:

     1 //递归实现先序遍历
     2 template <typename Entry>
     3 void binary_tree<Entry>::preordertraversal(binary_node* root,void (*visit)(Entry& bt))
     4 {
     5     if(root != null)
     6     {
     7         visit(root->data);
     8         preorder(root->lchild,visit);
     9         preorder(root->rchild,visit);
    10     }
    11 }

    栈实现前序遍历较简单,由于每次先输出根节点,再输出左节点随后是右节点。

    算法是:

    1、若栈非空输出根节点,并出栈
    2、将右节点压栈(如果存在)
    3、将左节点压栈(如果存在)
    4、重复第1步直到栈空
    注意:之所以先压右节点是考虑了栈的特性,这样在迭代过程中可以先拿到左节点处理。(栈的先入后出)

     1 template <typename Entry>
     2 void binary_tree<Entry>::preordertraversal(binary_node* root,void (*visit)(Entry& bt))
     3 {
     4     if(root == null) return;
     5     binary_node* cur = root;
     6     stack<binary_node*> s;
     7     s.push(cur);
     8     while(!s.empty())
     9     {
    10         cur = s.top();
    11         visit(cur->data);
    12         s.pop();
    13         if(cur->rchild != null) s.push(cur->rchild);
    14         if(cur->lchild != null) s.push(cur->lchild);
    15     }
    16 }

    再来看看中序遍历:

     1 //递归实现的中序遍历
     2 template <typename Entry>
     3 void binary_tree<Entry>::inordertraversal(binary_node* root,void (*visit)(Entry& bt))
     4 {
     5     if(root != null)
     6     {
     7         preorder(root->lchild,visit);
     8         visit(root->data);
     9         preorder(root->rchild,visit);
    10     }
    11 }

    栈的中序遍历需要套两层循环,由于需要先输出左节点,因此必须向下查找直到左节点为空才能输出。处理逻辑如下:

    1、如果栈顶元素非空且左节点存在,将其入栈,重复该过程。若不存在则进入第2步
    2、若栈非空,输出栈顶元素并出栈。判断刚出栈的元素的右节点是否存在,不存在重复第2步,存在则将右节点入栈,跳至第1步

     1 template <typename Entry>
     2 void binary_tree<Entry>::inordertraversal(binary_node* root,void (*visit)(Entry& bt))
     3 {
     4     if(root == null) return;
     5     binary_node* cur = root;
     6     stack<binary_node*> s;
     7     s.push(cur);
     8     while(!s.empty())
     9     {
    10         while(s.top()->lchild!=null)
    11             s.push(s.top()->lchild);
    12         while(!s.empty())
    13         {
    14             binary_node* cur = s.top();
    15             visit(cur->data);
    16             s.pop();
    17             if(cur->rchild != null)
    18             {
    19                 s.push(cur->rchild);
    20                 break;
    21             }
    22         }
    23     
    24     }
    25     
    26 }

    再来看看后序遍历:

     1 //递归实现的后序遍历
     2 template <typename Entry>
     3 void binary_tree<Entry>::postordertraversal(binary_node* root,void (*visit)(Entry& bt))
     4 {
     5     if(root != null)
     6     {
     7         preorder(root->lchild,visit);
     8         preorder(root->rchild,visit);
     9         visit(root->data);
    10     }
    11 }

    后序遍历在中序的双层循环的基础上需要加入一个记录,专门记录上一次出栈的节点。步骤如下:

    1、如果栈顶元素非空且左节点存在,将其入栈,重复该过程。若不存在则进入第2步(该过程和中序遍历一致)
    2、判断上一次出栈节点是否当前节点的右节点,或者当前节点是否存在右节点,满足任一条件,将当前节点输出,并出栈。否则将右节点压栈。跳至第1步

     1 template <typename Entry>
     2 void binary_tree<Entry>::postordertraversal(binary_node* root,void (*visit)(Entry& bt))
     3 {
     4     if(root == null) return;
     5     stack<binary_node*> s;
     6     s.push(root);
     7     binary_node* lastpop = null;
     8     while(!s.empty())
     9     {
    10         while(s.top()->lchild != null)
    11             s.push(s.top()->lchild);
    12         while(!s.empty())
    13         {
    14             if(lastpop == s.top()->rchild || s.top()->rchild == null)
    15             {
    16                 visit(s.top()->data);
    17                 lastpop = s.top();
    18                 s.pop();
    19             }
    20             else if(s.top()->rchild != null)
    21             {
    22                 s.push(s.top()->rchild);
    23                 break;
    24             }
    25         }
    26     }
    27 }
    if(lastpop == s.top()->rchild || s.top()->rchild == null)
    是判断上次弹出的结点是不是当前结点的右结点,或者当前节点没有右结点,因为访问次序是“左-右-中”。

    而二叉树的层次遍历可以通过队列来实现:

     1 //用队列来实现二叉树的层次遍历
     2 template <typename Entry>
     3 void binary_tree<Entry>::leveltraversal(binary_node* root,void (*visit)(Entry& bt))
     4 {
     5     if(root == null) return;
     6     queue<binary_node*> q;
     7     q.push(root);
     8     while(!q.empty())
     9     {
    10         visit(q.front());
    11         if(q.front()->lchild != null)
    12             q.push(q.front()->lchild);
    13         if(q.front()->rchild != null)
    14             q.push(q.front()->rchild);
    15         q.pop();
    16     }
    17 }

    转载说明:主要参考来源:https://www.jianshu.com/p/12848eef3452

  • 相关阅读:
    HDU 6043 KazaQ's Socks
    HDU 6033 Add More Zero
    D
    D
    L
    关于G
    excel函数实现下拉框控制显示不同内容
    牛客网暑期ACM多校训练营(第五场)J-plan (模拟)
    牛客网暑期ACM多校训练营(第五场)G-max (math)
    牛客网暑期ACM多校训练营(第五场)A-gap (二分答案)
  • 原文地址:https://www.cnblogs.com/jeavenwong/p/8176808.html
Copyright © 2011-2022 走看看