一、什么是树?什么又是二叉树
(1)树的定义
树是一种特殊的数据结构,是由N(N>=0)个节点构成的具有层次关系的集合。每棵树都有一个特殊的结点,就是根结点,根节点没有前驱结点;除过根节点的其余结点被分为若干个互不相交的集合,称之为子树;每个子树的根结点有且仅有一个前驱,可以有0个或者多个后继
(2)那么问题来了,什么是二叉树嘞?
二叉树是每个结点最多有两个子树的有序树(它由一个根节点加上它的左右子树组合起来的)。
* 特点:(1)每个结点最多有两棵子树,即二叉树不存在度大于2的结点(分支数最大不超过2)
(2)二叉树的子树有左右之分,其子树的次序不能被颠倒
* 二叉树有五种基本形态:(如下图所示)
(1)空二叉树
(2)只有一个根节点
(3)只有左子树
(4)只有右子树
(5)完全二叉树
- 满二叉树:一棵二叉树上的所有分支结点都存在左右子树,并且叶子结点都在同一层上面
- 完全二叉树:如果一个具有N个结点的二叉树与之满二叉树的前N个结点的结构相同,就是完全二叉树
二叉树的四种遍历
1、前序遍历(VLR):根节点—>左子树—>右子树
【递归遍历】
算法如下:
判断二叉树是否为空,倘若为空,则return;否则:
* (1)先访问根节点
* (2)然后前序遍历根节点的左子树
* (3)最后前序遍历根节点的右子树
代码实现:
//前序递归遍历
void PreOrder()
{
cout<<"前序遍历"<<endl;
_PreOrder(_pRoot);
cout<<endl;
}
//前序递归: 根---根的左子树---根的右子树
void _PreOrder(PNode& pRoot)
{
if(pRoot)
{
cout<< pRoot->_data << "";
_PreOrder(pRoot->_pLeft);
_PreOrder(pRoot->_pRight);
}
}
【非递归遍历】:(借助栈实现)
算法如下:
判断二叉树是否为空,倘若为空,则return;否则:
(1)初始化一个栈
(2)根节点入栈
(3)当栈不为空时,循环执行下列步骤
* 访问取出栈顶元素
* 倘若该被访问的节点的左子树非空时,将该结点左子树指针入栈
* 倘若该被访问的节点的右子树非空时,将该结点右子树指针入栈
(4)重复执行直至栈为空,return
代码实现:
//前序非递归
void PreOrder_N()
{
_PreOrder_N(_pRoot);
}
//前序非递归
void _PreOrder_N(PNode& pRoot)
{
//判断树是否存在
if(NULL == pRoot)
return;
stack<PNode> s;
s.push(pRoot);
while(!s.empty())
{
PNode pTop = s.top();
cout<<pTop->_data<<"";
if(pTop->_pLeft)
s.push(pTop->_pLeft);
if(pTop->_pRight)
s.push(pTop->_pRight);
}
cout<<endl;
}
2、中序遍历(LVR):左子树—>根节点—>右子树
【递归遍历】
算法如下:
判断二叉树是否为空,倘若为空,则return;否则:
* (1)先中序遍历根节点的左子树
* (2)访问根节点
* (3)最后中序遍历根节点的右子树
代码实现:
//中序递归遍历
void InOrder()
{
cout<<"中序遍历"<<endl;
_InOrder(_pRoot);
cout<<" "<<endl;
}
//中序递归: 左子树---根节点---右子树
void _InOrder(PNode pRoot)
{
if(pRoot)
{
_InOrder(pRoot->_pLeft);
cout<< pRoot->_data <<"";
_InOrder(pRoot->_pRight);
}
}
【非递归遍历】
算法如下:
判断二叉树是否为空,倘若为空,则return;否则:
(1)初始化一个栈
(2)给定一个任意节点pCur,倘若pCur的左孩子不为空时,则将其入栈,并将pCur指向pCur的左孩子;(一直循环执行,直至左孩子为空)
(3)执行完步骤(2)后,说明最左边的结点已被找到,访问输出栈顶元素,并进行出栈操作
(4)将pCur指向其右孩子
(5)重复2、3、4步骤,直至当前结点为空或者栈为空,return
代码实现:
//中序非递归
void InOrder_N()
{
_InOrder_N(_pRoot);
}
//中序非递归
void _InOrder_N(PNode pRoot)
{
if(NULL == pRoot)
return;
stack<PNode> s;
PNode pCur = pRoot;
while(pCur || !s.empty())
{
while(pCur)
{
s.push(pCur);
pCur = pCur->_pLeft;
}
pCur = s.top();
cout<<pCur->_data<<"";
s.pop();
pCur = pCur->_pRight;
}
}
3、后序遍历(LRV):左子树—>右子树—>根节点
【递归遍历】
算法如下:
判断二叉树是否为空,倘若为空,则return;否则:
* (1)先后序遍历根节点的左子树
* (2)后序遍历根节点的右子树
* (3)访问根节点
代码实现:
//后序递归遍历
void PostOrder()
{
cout<<"后序遍历"<<endl;
_PostOrder(_pRoot);
cout<<""<<endl;
}
//后序递归: 左子树---右子树---根节点
void _PostOrder(PNode pRoot)
{
if(pRoot)
{
_PostOrder(pRoot->_pLeft);
_PostOrder(pRoot->_pRight);
cout<< pRoot->_data <<"";
}
}
【非递归遍历】:出栈顺序为左右根,则压栈顺序为根右左
算法如下:
判断二叉树是否为空,倘若为空,则return;否则:
(1)初始化一个栈
(2)给定一个任意节点pCur
(3)将PCur入栈,倘若pCur的左孩子不为空时,将pCur指向pCur的左孩子;(一直循环执行,直至左孩子为空)
(4)执行完步骤(2)后,说明最左边的结点已被找到,访问栈顶元素
(5)此时倘若栈顶元素没有右孩子,或者有右孩子但已被访问输出;则直接打印输出该结点,并将其出栈
(6)否则当不满足(4)的情况时,将该结点的右孩子给pCur
(7)重复3、4、5、6步骤,直至栈为空,return
代码实现:
//后序非递归
void PostOrder_N()
{
_PostOrder_N(_pRoot);
}
//后序非递归
void _PostOrder_N(PNode pRoot)
{
if(NULL == pRoot)
return;
stack<PNode> s;
PNode pCur = pRoot;
PNode prev = NULL:
while()
{
while()
{
s.push(pCur);
pCur = pCur->_pLeft;
}
pTop = s.top();
if(NULL == pTop->_pRight || prev == pTop->_pRight)
{
cout<<pTop->_data<<"";
s.pop;
}
else
{
pCur = pTop->_pRight;
}
}
}
4、层序遍历(非递归)
层序遍历就是:从根节点到叶子结点按照同一层先左子树后右子树的次序遍历二叉树。层序遍历用队列的方式实现
算法如下:
(1)初始化一个队列
(2)将根节点的指针入队列,即插入队尾
(3)倘若队列非空,则循环执行下列步骤:
* 从对头取出一个结点访问
* 倘若被访问的结点非空,则让其左子树指针入队列
* 倘若被访问的结点非空,则让其左子树指针入队列
(4)队列清空后,return
代码实现:
//层序遍历
void LevelOrder()
{
cout<<"层序遍历"<<endl;
_LevelOrder(_pRoot);
cout<<""<<endl;
}
//层序遍历
void _LevelOrder(PNode pRoot)
{
if(pRoot)
{
queue<PNode> q;
q.push(pRoot);
while(!q.empty())
{
PNode pCur = q.front();
cout<< pCur->_data <<"";
q.pop();
if(pCur->_pLeft)
q.push(pCur->_pLeft);
if(pCur->_pRight)
q.push(pCur->_pRight);
}
}
}