二叉树递归遍历和非递归遍历

1. 二叉树遍历定义
遍历二叉树,即遵从某种次序访问二叉树中的所有节点,使得每个节点仅被访问一次。这里提到的“访问”是指对节点施行某种操作,可以是输出节点信息,修改节点值等,但要求这种访问不破坏它原来的数据结构。在本文中,遍历操作操作为访问并输出节点值,且以二叉链表作为二叉树的存贮结构。由于二叉树是一种非线性结构,每个节点可能有一个以上的直接后继,因此,必须规定遍历的规则,并按此规则遍历二叉树,最后得到二叉树所有节点的一个线性序列。


令L,R,D分别代表二叉树的左子树、右子树、根节点,则遍历二叉树有6种规则:DLR、DRL、LDR、LRD、RDL、RKD。若规定二叉树中必须先左后右(左右顺序不能颠倒),则只有DLR、LDR、LRD三种遍历规则。DLR称为前根遍历(或前序遍历、先序遍历、先根遍历),LDR称为中根遍历(或中序遍历),LRD称为后根遍历(或后序遍历)。


二叉树结构定义为:
typedef struct _BinaryTreeNode
{
int value;
struct _BinaryTreeNode* pLeft; //指向左子节点的指针
struct _BinaryTreeNode* pRight; //指向右子节点的指针
}BinaryTreeNode_t;


2.先根遍历
所谓先根遍历,就是根节点最先遍历,其次左子树,最后右子树。

2.1 递归遍历
先根遍历二叉树的递归遍历算法描述为:

若二叉树为空,则算法结束;否则
(1)输出根节点;
(2)先根遍历左子树;
(3)先根遍历右子树;


void PreOrder(BinaryTreeNode_t* pRoot)
{
BinaryTreeNode_t* p;

p = pRoot;

if (p != NULL)
{
std::cout<<p->value<<" ";
PreOrder(p->pLeft);
PreOrder(p->pRight);
}
}


2.2 非递归遍历
利用栈存储二叉树节点,算法思想为:
从二叉树根节点开始,沿左子树一直走到末端(左孩子为空)为止。在走的过程中,访问所遇节点,并依次把所遇节点进栈,当左子树为空时,从栈顶退出某节点,并将指针指向该节点的右孩子。如此重复,直到栈为空或指针为空止。
void PreOrder(const BinaryTreeNode_t* pRoot)
{
std::stack<BinaryTreeNode_t*> s;
BinaryTreeNode_t* p;
p = pRoot;


while(p != NULL || s.size()>0)
{
while(p != NULL)
{

//访问当前节点

std::cout<<p->value<<std::endl;


//将当前节点入栈
s.push(p);


//访问其左儿子
p = p->pLeft;
}


//处理完左儿子,再处理右子儿子
p = s.top();
s.pop();
p = p->pRight;
}
}


3. 中根遍历
所谓中根遍历,就是根在中间,先左子树,然后根节点,最后右子树。

3.1 递归遍历
中根遍历二叉树的递归遍历算法描述为:

若二叉树为空,则算法结束;否则
(1) 中根遍历左子树;
(2) 输出根节点;
(3) 中根遍历右子树;


void InOrder(BinaryTreeNode_t* pRoot)
{
BinaryTreeNode_t* p;
p = pRoot;
if (p != NULL)
{
InOrder(p->pLeft);
cout<<p->value<<" ";
InOrder(p->pRight);
}
}



3.2 非递归遍历
同样使用栈来存贮二叉树节点,算法思想为:
从二叉树根节点开始,沿左子树一直走到末端(左孩子为空)为止,在走的过程中,把依次遇到的节点进栈,待左子树为空时,从栈中退出节点并访问,然后再转向它的右子树。如此重复,直到栈空或指针为空止。


void InOrder(const BinaryTreeNode_t* pRoot)
{
stack<BinaryTreeNode_t*> s;
BinaryTreeNode_t* p;
p = pRoot;


while(p != NULL || s.size()>0)
{

//一直访问左儿子

while(p != NULL)
{
s.push(p);
p = p->pLeft;
}


//指向栈顶元素,即左儿子为NULL的节点
p = s.top();


//弹出该节点并访问
s.pop();
std::cout<<p->value<<std::endl;


//同样处理其右儿子
p = p->pRight;
}
}


4. 后根遍历
所谓后根遍历,就是根在最后,即先左子树,然后右子树,最后根节点。

4.1 递归遍历
后根遍历二叉树的递归遍历算法描述为:
若二叉树为空,则算法结束;否则
(1)后根遍历左子树:
(2)后根遍历历子树;
(3)访问根节点。


void PostOrder(BinaryTreeNode_t* pRoot)
{
BinaryTreeNode_t* p;
p = root;
if (p != NULL)
{
PostOrder(p->pLeft);
PostOrder(p->pRight);
cout<<p->value<<" ";
}
}


4.2 非递归遍历
利用栈来实现二叉树的后根遍历要比前序和中序遍历复杂得多。在后根遍历中,当搜索指针指向某一个节点时,不能马上进行访问,而先要遍历左子树,所以此节点应先进栈保存,当遍历完它的左子树后,再次回到该节点,还不能访问它,还需先遍历其右子树。只有等它的右子树遍历完后再次退栈时才能访问该节点。
void PostOrder(const BinaryTreeNode_t* pRoot)
{
stack<BinaryTreeNode_t*> s;
BinaryTreeNode_t* p;

BinaryTreeNode_t* pre = NULL;
p = pRoot;


while(p != NULL || s.size()>0)
{

//一直向左遍历左子节点
while(p != NULL)
{
s.push(p);
p = p->pLeft;
}

//指向栈顶元素,即最后一个左儿子为NULL的节点
p = s.top();
//p = p->pRight;


//如果该节点的右儿子也为空,则其为叶子节点,访问该节点

//如果该节点的右儿子已经被访问过,则访问该节点

if(p->pRight == NULL || p->pRight == pre)
{
//p = s.top();
std::cout<<p->value<<std::endl;
s.pop();

pre = p;
p = NULL;
}

//该节点右儿子不为空,同样遍历其右儿子为根节点的子树

else

p = p->pRight;
}
}

你可能感兴趣的:(二叉树,遍历,非递归)