二叉树的前序、中序、后序遍历(递归、非递归写法)

一、什么是二叉树?

二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个节点最多只能有两棵子树,且有左右之分。

二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个节点 。

本文主要介绍二叉树的前序、中序、后序三种遍历方式的递归写法及非递归写法。

二、二叉树的基本概念

①节点:包含一个数据元素及若干指向子树分支的信息。
②节点的度:一个节点拥有子树的数目称为节点的度。
③叶子节点:也称为终端节点,没有子树的节点或者度为零的节点。
④分支节点:也称为非终端节点,度不为零的节点称为非终端节点。
⑤树的度:树中所有节点的度的最大值。
⑥节点的层次:从根节点开始,假设根节点为第1层,根节点的子节点为第2层,依此类推,如果某一个节点位于第L层,则其子节点位于第L+1层。
⑦树的深度:也称为树的高度,树中所有节点的层次最大值称为树的深度。
⑧有序树:如果树中各棵子树的次序是有先后次序,则称该树为有序树。
⑨无序树:如果树中各棵子树的次序没有先后次序,则称该树为无序树。
⑩森林:由m(m≥0)棵互不相交的树构成一片森林。如果把一棵非空的树的根节点删除,则该树就变成了一片森林,森林中的树由原来根节点的各棵子树构成。

三、二叉树的三种遍历方式


以此二叉树为例:

1.前序遍历(preordertraversal)

二叉树的前序遍历顺序为根→左→右,即运用递归算法时,先走该点(根节点)本身,再走左子树,再走右子树。
对于示例中给出的二叉树,前序遍历结果为:A B D G H C E F I

1.中序遍历(inordertraversal)

二叉树的中序遍历顺序为左→根→右,即运用递归算法时,先走该点的左子树,再走(根节点)本身,再走右子树。
对于示例中给出的二叉树,中序遍历结果为:B G D H A E C I F

1.后序遍历(postordertraversal)

二叉树的后序遍历顺序为左→右→根,即运用递归算法时,先走该点左子树,再走右子树,再走(根节点)本身。
对于示例中给出的二叉树,后序遍历结果为:G H D B E I F C A

四、代码实现

1.建树过程

首先建立图中所示的二叉树。

class MyTreeNode{
	public:
		char data;
		MyTreeNode* leftchild;
		MyTreeNode* rightchild;
		MyTreeNode(char data)
		{
			this->data=data;
			this->leftchild=NULL;
			this->rightchild=NULL;
		}
		void SetChild(MyTreeNode* leftchild,MyTreeNode* rightchild)
		{
			this->leftchild=leftchild;
			this->rightchild=rightchild;
		}
};
int main()
{
	MyTreeNode* mnA=new MyTreeNode('A');
	MyTreeNode* mnB=new MyTreeNode('B');
	MyTreeNode* mnC=new MyTreeNode('C');
	MyTreeNode* mnD=new MyTreeNode('D');
	MyTreeNode* mnE=new MyTreeNode('E');
	MyTreeNode* mnF=new MyTreeNode('F');
	MyTreeNode* mnG=new MyTreeNode('G');
	MyTreeNode* mnH=new MyTreeNode('H');
	MyTreeNode* mnI=new MyTreeNode('I');
	
	mnA->SetChild(mnB,mnC);
	mnB->SetChild(NULL,mnD);
	mnC->SetChild(mnE,mnF);
	mnD->SetChild(mnG,mnH);
	mnF->SetChild(mnI,NULL);
	
	MyTreeNode* root=mnA;
	return 0;
}

2.递归写法

递归写法的思路相对较为简单,三种遍历方式只要调换语句的执行顺序即可,这里直接给出代码。

void preorderTraversal(MyTreeNode* root) //前序遍历
{
	if(!root) return;
	cout<<root->data<<' ';
	preorderTraversal(root->leftchild);
	preorderTraversal(root->rightchild);
}
void inorderTraversal(MyTreeNode* root)//中序遍历
{
    if(!root) return;
    inorderTraversal(root->leftchild);
	cout<<root->data<<' ';
	inorderTraversal(root->rightchild);
}
void postorderTraversal(MyTreeNode* root)//后续遍历
{
    if(!root) return;
	postorderTraversal(root->leftchild);
	postorderTraversal(root->rightchild);
	cout<<root->data<<' ';
}

3.非递归写法

非递归写法的执行效率要高于递归写法,但相对来说思路会较为复杂,需要用到栈这一数据结构。

void preorderTraversal(MyTreeNode* root) //前序遍历
{
    vector<char> ans;
    if(!root) return;
    stack<MyTreeNode*> s;
    s.push(root);
    while (!s.empty())
    {
        MyTreeNode* node=s.top();
        ans.push_back(node->data);
        s.pop();
        if (node->rightchild!=NULL) s.push(node->rightchild);
        if (node->leftchild!=NULL) s.push(node->leftchild);
    }
    for(auto x:ans) cout<<x<<' ';
    cout<<endl;
}
void inorderTraversal(MyTreeNode* root)//中序遍历
{
    vector<char> ans;
    if (!root) return;
    stack<MyTreeNode*> s;
    MyTreeNode* node=root;
    while(!s.empty()||node!=NULL)
    {
        while(node!=NULL)
        {
            s.push(node);
            node=node->leftchild;
        }
        if (!s.empty())
        {
            node=s.top();
            ans.push_back(node->data);
            s.pop();
            node=node->rightchild;
        }
    }
    for(auto x:ans) cout<<x<<' ';
    cout<<endl;
}
void postorderTraversal(MyTreeNode* root)//后续遍历
{
    vector<char> ans;
    if(!root) return;
    stack<MyTreeNode*> s;
    s.push(root);
    while (!s.empty())
    {
        MyTreeNode* node=s.top();
        ans.push_back(node->data);
        s.pop();            
        if (node->leftchild!=NULL) s.push(node->leftchild);
        if (node->rightchild!=NULL) s.push(node->rightchild);
    }
    reverse(ans.begin(), ans.end());
    for(auto x:ans) cout<<x<<' ';
    cout<<endl;
}

其中后序遍历使用了一个小技巧,即先使用类似于前序遍历的写法,将根→右→左的顺序储存在结果vector中,最后再使用reverse函数返回倒序,这样可以极大地简化整个过程。

4.完整代码

#include<iostream>
#include<algorithm>
#include<vector> 
#include<stack> 
using namespace std;
class MyTreeNode{
	public:
		char data;
		MyTreeNode* leftchild;
		MyTreeNode* rightchild;
		MyTreeNode(char data)
		{
			this->data=data;
			this->leftchild=NULL;
			this->rightchild=NULL;
		}
		void SetChild(MyTreeNode* leftchild,MyTreeNode* rightchild)
		{
			this->leftchild=leftchild;
			this->rightchild=rightchild;
		}
};
void preorderTraversal(MyTreeNode* root) 
{
    vector<char> ans;
    if(!root) return;
    stack<MyTreeNode*> s;
    s.push(root);
    while (!s.empty())
    {
        MyTreeNode* node=s.top();
        ans.push_back(node->data);
        s.pop();
        if (node->rightchild!=NULL) s.push(node->rightchild);
        if (node->leftchild!=NULL) s.push(node->leftchild);
    }
    for(auto x:ans) cout<<x<<' ';
    cout<<endl;
}
void inorderTraversal(MyTreeNode* root)
{
    vector<char> ans;
    if (!root) return;
    stack<MyTreeNode*> s;
    MyTreeNode* node=root;
    while(!s.empty()||node!=NULL)
    {
        while(node!=NULL)
        {
            s.push(node);
            node=node->leftchild;
        }
        if (!s.empty())
        {
            node=s.top();
            ans.push_back(node->data);
            s.pop();
            node=node->rightchild;
        }
    }
    for(auto x:ans) cout<<x<<' ';
    cout<<endl;
}
void postorderTraversal(MyTreeNode* root)
{
    vector<char> ans;
    if(!root) return;
    stack<MyTreeNode*> s;
    s.push(root);
    while (!s.empty())
    {
        MyTreeNode* node=s.top();
        ans.push_back(node->data);
        s.pop();            
        if (node->leftchild!=NULL) s.push(node->leftchild);
        if (node->rightchild!=NULL) s.push(node->rightchild);
    }
    reverse(ans.begin(), ans.end());
    for(auto x:ans) cout<<x<<' ';
    cout<<endl;
}
int main()
{
	MyTreeNode* mnA=new MyTreeNode('A');
	MyTreeNode* mnB=new MyTreeNode('B');
	MyTreeNode* mnC=new MyTreeNode('C');
	MyTreeNode* mnD=new MyTreeNode('D');
	MyTreeNode* mnE=new MyTreeNode('E');
	MyTreeNode* mnF=new MyTreeNode('F');
	MyTreeNode* mnG=new MyTreeNode('G');
	MyTreeNode* mnH=new MyTreeNode('H');
	MyTreeNode* mnI=new MyTreeNode('I');
	
	mnA->SetChild(mnB,mnC);
	mnB->SetChild(NULL,mnD);
	mnC->SetChild(mnE,mnF);
	mnD->SetChild(mnG,mnH);
	mnF->SetChild(mnI,NULL);
	
	MyTreeNode* root=mnA;
	
	cout<<"先序遍历:"; 
	preorderTraversal(root);
	cout<<"中序遍历:"; 
	inorderTraversal(root);
	cout<<"后序遍历:";
	postorderTraversal(root);
	return 0;
}
/*
先序遍历:A B D G H C E F I
中序遍历:B G D H A E C I F
后序遍历:G H D B E I F C A
*/

运行结果:
在这里插入图片描述

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值