复习一下数据结构和算法,虽然以前都学过,但是许久不碰还是会有些生疏。
我找了半天二叉树遍历模板都有问题,思路不清晰,乱七八糟不知道在干嘛。我记得之前二叉树模板挺好找的,百度搜索最近怎么回事。之前也是找模板找不到,折腾了很久。
首先建树
//ABDH##I##EJ##K##CF#L##G##
Tree *CreateTree()
{
char ch;
cin>>ch;
if(ch == '#')
{
return NULL;
}
else
{
Tree *T;
T = (Tree *)malloc(sizeof(Tree));
T->data = ch;
T->lchild = CreateTree();
T->rchild = CreateTree();
return T;
}
}
递归版本的遍历:
前序遍历
void PreOrder(Tree *T)
{
if(T)
{
cout<<T->data<<" ";
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
中序遍历
void InOrder(Tree *T)
{
if(T)
{
InOrder(T->lchild);
cout<<T->data<<" ";
InOrder(T->rchild);
}
}
后序遍历
void PostOrder(Tree *T)
{
if(T)
{
PostOrder(T->lchild);
PostOrder(T->rchild);
cout<<T->data<<" ";
}
}
递归代码非常清晰,简单。
无论什么遍历顺序,遍历节点顺序实质上就类似于深度优先搜索,差别只是在输出节点顺序。
前序遍历的就是第一次遇到节点就输出,中序遍历是输出该节点的左子树后再输出节点值,后序遍历是输出节点的左子树和右子树之后再输出节点值。
接下来是非递归代码,这一块有点麻烦。
首先我们看先序遍历
void NonRecPreOrder(Tree *T)
{
Tree *p = T;
stack<Tree *> s;
while(p != NULL || !s.empty())
{
if(p != NULL)
{
cout<<p->data<<" ";
s.push(p);
p = p->lchild;
}
else
{
p = s.top();
s.pop();
p = p->rchild;
}
}
}
其实思路上是差不多的,用栈去模拟了对二叉树的深度优先搜索,先搜索左节点,将之入栈,等到左节点全部入栈后,遍历节点已经没有左节点的时候,搜索遍历右节点,如果不为空就遍历。
再看中序遍历
void NonRecInOrder(Tree *T)
{
Tree *p = T;
stack<Tree *> s;
while(p != NULL || !s.empty())
{
if(p != NULL)
{
s.push(p);
p = p->lchild;
}
else
{
p = s.top();
s.pop();
cout<<p->data<<" ";
p = p->rchild;
}
}
}
大体差不多,只是输出值的地方不同,先序是在第一次把值压入栈之前输出,中序是遍历左节点之后,遍历右节点之前输出。
接下来是后序遍历这个要难一点。
void NonRecPostOrder(Tree *T)
{
Tree *p = T;
Tree *pre = NULL;
stack<Tree *> s;
while(p != NULL || !s.empty())
{
if(p != NULL)
{
s.push(p);
p = p->lchild;
}
else
{
p = s.top();
if(p->rchild==NULL || p->rchild==pre)
{
cout<<p->data<<" ";
pre = p;
p = NULL;
s.pop();
}
else
{
p = p->rchild;
}
}
}
}
前中序和后序不同的一个地方在于,前序和中序在需要输出值的时候,节点还在栈中。但是后序是在左节点和右节点都输出过后才输出。所以需要一个指针变量pre去标记右节点是否便利过,左节点肯定遍历过,因为是深度优先遍历。
指针写法还是很麻烦的需要考虑很多种情况,不然很容易就错了。