二叉树的三种遍历方式,
前序遍历
,中序遍历
,后序遍历
,中的前中后都是指的是根节点的访问顺序,这三种遍历方式的概念在这里就不多说了,太普遍了!
二叉树的建立
我们这里以前序遍历为例:
我们先定义好结构体
struct Tree{
Tree* lson;
Tree* rson;
int data;
};
Tree* T;
下面是前序建立二叉树:
void createBtiTree(Tree* &tree)
{
int data;
cin>>data;
if(data==-1) {
tree=NULL;
}else {
tree = new Tree();
tree->data = data;
createBtiTree(tree->lson);
createBtiTree(tree->rson);
}
}
二叉树的递归方式遍历
当我们觉得构造递归困难的时候(不知道在这里用构造这个词对不对),我们就可以把递归想象成为一棵树,这样就容易构造一些!
递归的三种遍历方式比较基础,在这里就不过多进行赘述,直接给出代码!如果读者有困难的话,可以试着在纸上描述一下,毕竟递归是要深入的东西!
前序遍历
void preOrderRecur(Tree* tree)
{
if(tree==NULL) return ;
cout<<tree->data<<" ";
preOrderRecur(tree->lson);
preOrderRecur(tree->rson);
}
中序遍历
void inOrderRecur(Tree* tree)
{
if(tree==NULL) return ;
inOrderRecur(tree->lson);
cout<<tree->data<<" ";
inOrderRecur(tree->rson);
}
后序遍历
void posOrderRecur(Tree* tree)
{
if(tree==NULL) return ;
posOrderRecur(tree->lson);
posOrderRecur(tree->rson);
cout<<tree->data<<" ";
}
非递归形式的二叉树的三种遍历方式
用递归解决的问题都能用非递归的方式解决。因为递归是使用函数栈来保存信息的,如果自己用自己申请的数据结构来代替函数栈就能实现相同的功能!
前序遍历
前序遍历也是最简单的一种,分为下面三个步骤:
- 申请一个栈,将头结点压入栈
- 弹出栈顶指针,记作:
cur
,如果这个这个指针有右孩子,将右孩子入栈,如果有左孩子,将左孩子入栈; - 不断重复过程2,直到栈为空,结束程序!
实现代码
void PreOrderRecur(Tree* tree)
{
while(!sta.empty()) sta.pop();
sta.push(tree);
while(!sta.empty()) {
Tree* cur = sta.top();
sta.pop();
cout<<cur->data<<" ";
if(cur->rson!=NULL) sta.push(cur->rson);
if(cur->lson!=NULL) sta.push(cur->lson);
}
}
中序遍历
- 申请一个栈,头结点为开始节点(当前节点)
- 如果当前节点不为空,那么将左节点压栈,即做
tree=tree->lson
操作,如果当前节点为空的时候打印栈顶元素,并且出栈,将 当前节点变为栈顶元素的右节点也就是做tree = cur->rson
(中序遍历中,栈主要保存的是父节点元素) - 不断重复步骤2直到栈空,结束程序!
实现代码
void InOrderRecur(Tree* tree)
{
while(!sta.empty()) sta.pop();
while(!sta.empty() || tree!=NULL) {
if(tree==NULL) {
Tree* cur = sta.top();
sta.pop();
cout<<cur->data<<" ";
tree=cur->rson;
} else {
sta.push(tree);
tree=tree->lson;
}
}
}
后序遍历
后序遍历用栈实现起来相对前面两种遍历是难实现一些,这这里给出两个方法:第一种方法是用两个栈来实现的(比较好理解),第二种是用一个栈来实现的!
两个栈的方法实现
先说两个栈来实现的方法,第一个栈保存的是根节点元素,第二栈是保存输出的元素!过程如下:
- 申请一个栈,将根节点入栈
- 如果栈不为空,弹出第一个栈的栈顶元素记做
cur
,将第一个栈顶元素出栈,然后将cur
压入第二个栈。如果cur
有左孩子将左孩子加入第一个栈,如果有右孩子将右孩子加入第一个栈 - 不断的重复步骤2,直到第一个栈为空,打印第二个栈,结束程序!
实现代码
void PosOrderRecur(Tree* tree)
{
while(!sta.empty()) sta.pop();
while(!Sta.empty()) Sta.pop();
sta.push(tree);
while(!sta.empty()) {
Tree* cur = sta.top();
sta.pop();Sta.push(cur);
if(cur->lson!=NULL) sta.push(cur->lson);
if(cur->rson!=NULL) sta.push(cur->rson);
}
while(!Sta.empty()) {
cout<<Sta.top()->data<<" ";
Sta.pop();
}
}
一个栈的实现
我们用cur
表示栈顶元素,h
表示的是最近栈的元素,初始化时h为头结点。算法流程如下:
- 申请一个栈 ,将头结点压栈,初始化
h
变量, - 如果栈不为空,
cur
赋为栈顶元素!
---- 1.如果cur
的左孩子不为NULL
并且h
不等于cur
的左孩子也不等于cur
的右孩子那么就将左孩子入栈。(如果最近h等于当前节点的左孩子,就说明左子树已经打印完了,否则就代表还没有打印过,就应该将左孩子或者右孩子入栈)
---- 2.在1条件不成立的条件下,并且cur
的右孩子不等于h
并且不为空,就说明右子树还没有处理过,这个时候就应该将cur
的右孩子入栈!
---- 3.如果前俩个条件都不成立,就说明cur
的左子树和右子树已经打印完毕了,或者当前节点为叶子节点,此时就应该将栈顶元素出栈了,并且令h
=cur
- 一直重复步骤2直到栈为空,结束程序
实现代码
void TPosOrderRecur(Tree* tree)
{
while(!sta.empty()) sta.pop();
sta.push(tree);
Tree* c=tree;
while(!sta.empty()) {
Tree* cur = sta.top();
if(cur->lson!=NULL && cur->lson!=c && cur->rson!=c){
sta.push(cur->lson);
} else if(cur->rson!=NULL && c!=cur->rson) {
sta.push(cur->rson);
} else{
sta.pop();
c=cur;
cout<<cur->data<<" ";
}
}
}
如果对我的文章感兴趣可以关注的公众号:云影原生。