原文链接:二叉树的遍历(基于栈的非递归方式实现)
二叉树的遍历(基于栈的非递归方式实现)
在写二叉树的时候如果用递归实现二叉树的遍历很简单,但是用非递归来实现二叉树的遍历就不那么简单了需要一些技巧。
那为什么还要非递归实现呢?个人理解:如果树的高度很大,超过了允许递归的次数,那么就会出错,比如我记得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 }
整个程序完全源代码如下:
#include<iostream>
#include<queue>
#include<stack>
using namespace std;
struct btnode{
int value;
btnode *left;
btnode *right;
};
void create(btnode *&root,int arr[],int length);
void addBTNode(btnode *&myBT,int val);
void preshow(btnode *root);//前序遍历
void inordershow(btnode *root);//中序遍历
void postshow(btnode *root);//后序遍历
int main(){
btnode *myBT = nullptr;
int arr[] = {12,2,15,1,3,18,16};
create(myBT,arr,sizeof(arr)/sizeof(int));
preshow(myBT);
cout<<endl;
inordershow(myBT);
cout<<endl;
postshow(myBT);
cout<<endl;
return 0;
}
void create(btnode *&root,int arr[],int length){
for(int i=0;i<length;i++)
addBTNode(root,arr[i]);
}
void addBTNode(btnode *&myBT,int val){
if(myBT == nullptr){
myBT = new btnode();
myBT->value = val;
myBT->left = nullptr;
myBT->right = nullptr;
return;
}
if(val == myBT->value){
return;
}
else if(val < myBT->value){
addBTNode(myBT->left,val);
}
else{
addBTNode(myBT->right,val);
}
}
void preshow(btnode *root){//前序遍历
if(root == nullptr)return;
stack<btnode*> s;
s.push(root);
btnode *cur;
while(!s.empty()){
cur = s.top();
cout<<s.top()->value<<" ";
s.pop();
if(cur->right != nullptr)
s.push(cur->right);
if(cur->left != nullptr)//注意都是if
s.push(cur->left);
}
}
void inordershow(btnode *root){//中序遍历
if(root == nullptr)return;
stack<btnode*> s;
s.push(root);
btnode *cur;
while(!s.empty()){
while(s.top()->left != nullptr)
s.push(s.top()->left);
while(!s.empty()){
cur = s.top();
cout<<s.top()->value<<" ";
s.pop();
if(cur->right != nullptr){
s.push(cur->right);
break;
}
}
}
}
void postshow(btnode *root){//后序遍历
if(root == nullptr)return;
stack<btnode*> s;
s.push(root);
btnode *lastnode;
while(!s.empty()){
while(s.top()->left != nullptr)
s.push(s.top()->left);
while(!s.empty()){
if(s.top()->right == nullptr || lastnode == s.top()->right){
lastnode = s.top();
cout<<s.top()->value<<" ";
s.pop();
}
else if(s.top()->right != nullptr){//注意这里要是else if !!!
s.push(s.top()->right);
break;
}
}
}
}
程序运行结果如下: