zoukankan      html  css  js  c++  java
  • 二叉树遍历(前序、中序、后序)的递归及非递归实现(小结)

    先写点最基本的知识

    前序遍历: 
        1.访问根节点 
        2.前序遍历左子树 
        3.前序遍历右子树 
    中序遍历: 
        1.中序遍历左子树 
        2.访问根节点 
        3.中序遍历右子树 
    后序遍历: 
        1.后序遍历左子树 
        2.后序遍历右子树 
        3.访问根节点

    递归实现是很容易地,可以参考《算法导论》上的写法

     1 //遍历
     2 //前序、中序、后序遍历
     3 PREVORDER-TREE-WALK(x)
     4   if x != NIL
     5      then print key[x]
     6           PREVORDER-TREE-WALK(left[x])          
     7           PREVORDER-TREE-WALK(right[x])
     8 //中序
     9 INORDER-TREE-WALK(x)
    10   if x != NIL
    11      then INORDER-TREE-WALK(left[x])
    12           print key[x]
    13           INORDER-TREE-WALK(right[x])
    14 //后序
    15 POST-TREE-WALK(x)
    16   if x != NIL 
    17      then POST-TREE-WALK(left[x])
    18           POST-TREE-WALK(right[x])
    19           print key[x]

    但看到这篇博文(程序员基本功之遍历二叉树)中的写法,感觉很舒服,也拿过来

    1 def VisitTree_Recursive(root, order):
    2   if root:
    3     if order == 'NLR': print(root.data)
    4     VisitTree_Recursive(root.left, order)
    5     if order == 'LNR': print(root.data)
    6     VisitTree_Recursive(root.right, order)
    7     if order == 'LRN': print(root.data)

    上面都是铺垫,递归写法很简单,面试中也不会被问到,会拿来做问题的是非递归写法。感觉(程序员基本功之遍历二叉树)这篇博文对思路的分析非常清楚,也拿过来

    非递归的前序、中序遍历

    如果不用递归呢?实际上我们要做的就是自己维护一个栈(数据结构)来保存需要但尚未来得及处理的数据。

    前序和中序都是非常简单的,当遇到一个非空的根结点时,打印其数据(如果是前序遍历),并将其压栈,然后递归地(这里用循环来模拟递归)处理其左子结点;当没有左子结点时,从栈中弹出之前遇到的某个根结点(它没有做子结点,或者左子结点已经处理完毕,需要再处理右子结点),打印数据(如果是中序遍历),然后继续处理右子结点。同样地,把两种遍历方式写在一起以便比较。

     1 def VisitTree(root, order):
     2   s = []
     3   while root or s:
     4     if root:
     5       if order == 'NLR': print(root.data)
     6       s.append(root)
     7       root = root.left
     8     else:
     9       root = s.pop()
    10       if order == 'LNR': print(root.data)
    11       root = root.right

    博主的python代码果然是清晰易懂,但看到的另外一篇文章(二叉树的非递归前序、中序以及后序遍历C++模版类实现)给出了实现代码,感觉更爽,代码拿过来。

    这是基于模板实现的Stack类

     1 #include <iostream>
     2 
     3 using namespace std;
     4 
     5 //Author: 过往记忆
     6 //Blog: www.wypblog.com
     7 //Email: wyphao.2007@163.com
     8 ///////////////////////////////////////////////////////////////////////
     9 //stack 
    10 template <class T>
    11 class Stack{
    12 public:
    13     Stack(int size = 50);
    14     ~Stack();
    15     void push(T* data);
    16     T* pop();
    17     bool isEmpty();
    18     T* peek();
    19 private:
    20     int size;
    21     int top;
    22     bool isFull();
    23     T **data;
    24 };
    25 
    26 template <class T>
    27 Stack<T>::Stack(int size){
    28     if(size <= 0){
    29         cout << "分配的内存太小了" << endl; 
    30     }
    31     
    32     data = new T*[size];
    33     top = -1;
    34     this->size = size; 
    35 }
    36 
    37 template <class T>
    38 Stack<T>::~Stack(){
    39     delete []data;
    40 }
    41 
    42 template <class T>
    43 void Stack<T>::push(T* data){
    44     ++top;
    45     if(isFull()){
    46         cout << "貌似栈满了耶" << endl;
    47         exit(1); 
    48     } 
    49     this->data[top] = data; 
    50 } 
    51 
    52 template <class T>
    53 T* Stack<T>::pop(){ 
    54     if(isEmpty()){
    55         cout << "栈为空,不可以再出元素了!" << endl;
    56         exit(1); 
    57     }
    58     
    59     return data[top--]; 
    60 }
    61 
    62 template <class T>
    63 T* Stack<T>::peek(){
    64     if(isEmpty()){
    65         cout << "栈为空" << endl;
    66         exit(1); 
    67     }
    68     
    69     return data[top];
    70 }
    71 
    72 template <class T>
    73 bool Stack<T>::isFull(){
    74     if(top == size){
    75         return true; 
    76     } 
    77     
    78     return false; 
    79 } 
    80 
    81 template <class T>
    82 bool Stack<T>::isEmpty(){
    83     if(top < 0){
    84         return true; 
    85     } 
    86     
    87     return false; 
    88 } 

    下面是使用此Stack实现的非递归版本遍历

     1 template <class T> 
     2 class BTree{
     3 public:
     4     BTree *left;
     5     BTree *right;
     6     T data; 
     7     
     8     BTree() : left(NULL), right(NULL), data(NULL){};
     9     ~BTree(){}; 
    10 }; 
    11 
    12 ///////////////////////////////////////////////////////////////////////
    13 template <class T> 
    14 void PreOrder(BTree<T> *root){
    15     if(root != NULL){
    16         Stack<BTree<T> > stack ;
    17         BTree<T> *ptr = root;
    18         BTree<T> *temp; 
    19         stack.push(ptr);
    20         while(!stack.isEmpty())    {
    21             temp =  stack.pop(); 
    22             cout << temp->data << " ";
    23             if(temp->right != NULL){
    24                 stack.push(temp->right);
    25             }
    26             
    27             if(temp->left != NULL){
    28                 stack.push(temp->left);
    29             }
    30         }
    31         cout << endl; 
    32     } 
    33 } 
    34 
    35 ///////////////////////////////////////////////////////////////////////
    36 template <class T>
    37 void InOrder(BTree<T> *root){
    38     if(root != NULL){
    39         Stack<BTree<T> > stack ;
    40         BTree<T> *ptr = root;
    41         while(!stack.isEmpty() || ptr != NULL){
    42             while(ptr != NULL){
    43                 stack.push(ptr);
    44                 ptr = ptr->left;
    45             }
    46             
    47             if(!stack.isEmpty()){
    48                 ptr = stack.pop();
    49                 cout << ptr->data << " ";
    50                 ptr = ptr->right;
    51             }
    52             
    53         }
    54         cout << endl; 
    55     }
    56 }
    57 
    58 ///////////////////////////////////////////////////////////////////////
    59 template <class T>
    60 void PostOrder(BTree<T> *root){
    61     if(root != NULL){
    62         Stack<BTree<T> > stack;
    63         BTree<T> *ptr = root;
    64         BTree<T> *temp;
    65         bool flags;
    66         
    67         do{
    68             while(ptr != NULL){
    69                 stack.push(ptr);
    70                 ptr = ptr->left;
    71             }
    72             
    73             temp = NULL;
    74             flags = true;
    75             
    76             while(flags && !stack.isEmpty()){
    77                 ptr = stack.peek();
    78                 if(ptr->right == temp){
    79                     cout << ptr->data << " ";
    80                     stack.pop();
    81                     temp = ptr;
    82                 }else{
    83                     ptr = ptr->right;
    84                     flags = false;
    85                 }
    86             }
    87         }while(!stack.isEmpty());
    88         cout << endl;        
    89     }
    90 }

    再次引用(程序员基本功之遍历二叉树)中分析。

    后序遍历要稍微复杂一点点,在前序和中序遍历的程序中,当我们准备进入根结点的右子树时,根结点就被扔出栈外了。但在后序遍历时,我们仍需保留它,直到右子树处理完毕。实际上当左子树遍历完成、或者右子树遍历完成时,我们都会在栈里看到根结点,为了区分这两种状态,添加一个临时变量记录前一次访问的结点,如果前一个结点是根结点的右子树,就说明左右子树全都遍历完成了。这也是以上PostOrder采用的方式。


    看到程序员基本功之遍历二叉树中介绍二叉树的层序遍历,搜集一下,以备参考

    非递归的时候,层序遍历使用的是队列,而非栈。处理过程非常简明,遇到一个结点,打印信息,然后依次将左、右子结点加入队列等待后续处理。

     1 from collections import deque
     2 
     3 def VisitTree_LevelOrder(root):
     4   if not root: return
     5   q = deque([root])
     6   while q:
     7     root = q.popleft()
     8     print(root.data)
     9     if root.left: q.append(root.left)
    10     if root.right: q.append(root.right)

    这篇博文(数据结构(六)——二叉树 前序、中序、后序、层次遍历及非递归实现 查找、统计个数、比较、求深度的递归实现)给出了层序遍历的实现代码,如下

    首先是自定义队列

     1 #define MAX 1000  
     2   
     3 typedef struct seqqueue{  
     4     bintree data[MAX];  
     5     int front;  
     6     int rear;  
     7 }seqqueue;  
     8   
     9   
    10 void enter(seqqueue *q,bintree t){  
    11     if(q->rear == MAX){  
    12         printf("the queue is full!
    ");  
    13     }else{  
    14         q->data[q->rear] = t;  
    15         q->rear++;  
    16     }  
    17 }  
    18   
    19 bintree del(seqqueue *q){  
    20     if(q->front == q->rear){  
    21         return NULL;  
    22     }else{  
    23         q->front++;  
    24         return q->data[q->front-1];  
    25     }  
    26 }  

    然后是层序遍历,和上面提到的层序遍历一个道理

     1 void level_tree(bintree t){  
     2     seqqueue q;  
     3     bintree temp;  
     4     q.front = q.rear = 0;  
     5     if(!t){  
     6         printf("the tree is empty
    ");  
     7         return ;  
     8     }  
     9     enter(&q,t);  
    10     while(q.front != q.rear){  
    11         t=del(&q);  
    12         printf("%c ",t->data);  
    13         if(t->lchild){  
    14             enter(&q,t->lchild);  
    15         }  
    16         if(t->rchild){  
    17             enter(&q,t->rchild);  
    18         }  
    19     }  
    20 }  

    还提到二叉树的其他常用算法,一并拿过来

    统计结点个数

    1 int count_tree(bintree t){  
    2     if(t){  
    3         return (count_tree(t->lchild)+count_tree(t->rchild)+1);  
    4     }  
    5     return 0;  
    6 }

    比较两个树是否相同

     1 int is_equal(bintree t1,bintree t2){  
     2     if(!t1 && !t2){      //都为空就相等  
     3         return 1;  
     4     }  
     5     if(t1 && t2 && t1->data == t2->data){      //有一个为空或数据不同就不判断了  
     6         if(is_equal(t1->lchild,t2->lchild))  
     7             if(is_equal(t1->rchild,t2->rchild)){  
     8                 return 1;  
     9             }  
    10     }  
    11     return 0;  
    12 }  

    求二叉树的深度

     1 int hight_tree(bintree t){  
     2     int h,left,right;  
     3     if(!t){  
     4         return 0;  
     5     }  
     6     left = hight_tree(t->lchild);  
     7     right = hight_tree(t->rchild);  
     8     h = (left>right?left:right)+1;  
     9     return h;  
    10 }

    参考博文:

    1.程序员基本功之遍历二叉树

    2.二叉树的非递归前序、中序以及后序遍历C++模版类实现

    3.数据结构(六)——二叉树 前序、中序、后序、层次遍历及非递归实现 查找、统计个数、比较、求深度的递归实现

    作者:RonaldHan
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    FZU 2098 刻苦的小芳(卡特兰数,动态规划)
    卡特兰数总结
    FZU 1064 教授的测试(卡特兰数,递归)
    HDU 4745 Two Rabbits(区间DP,最长非连续回文子串)
    Java 第十一届 蓝桥杯 省模拟赛 正整数的摆动序列
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 凯撒密码加密
    Java 第十一届 蓝桥杯 省模拟赛 凯撒密码加密
  • 原文地址:https://www.cnblogs.com/ronaldhan/p/3303640.html
Copyright © 2011-2022 走看看