/************************************************************************/
/* 包括内容:
1、二叉树的建立
2、二叉树的遍历(先序、中序和后序的递归、非递归遍历)
3、二叉树的层次遍历
4、二叉树的深度
5、二叉树的重建
*/
/************************************************************************/
#include <iostream>
#include <stack>
using namespace std;
struct TreeNode
{
int val;
TreeNode *lchild,*rchild;
TreeNode(int x):val(x),lchild(NULL),rchild(NULL){};
};
//递归方式,为了建成树后的访问,使用引用
void treeCreate(TreeNode * &T)
{
char ch;
cin>>ch;
// # 表示此节点为空
if (ch=='#')
{
T=NULL;
}
else{
int i=ch-'0';
T=new TreeNode(i);
treeCreate(T->lchild);//利用lchild和rchild将树连接起来
treeCreate(T->rchild);
}
}
// 非递归方式,则要用到栈,但问题是什么时候进栈,什么时候出战???
//所以如果是用()方式输入,这样当遇到左括号的时候入栈,右括号出战,逗号表示是右子树
void treeCreateNoneR(TreeNode * &T)
{
char ch;
int lev=0;//记录左右子树存在情况
stack<TreeNode *> sta;
T=NULL;//将T先置为空,指向新产生的根指针,整棵树才能建立起来
TreeNode *t;
while (cin>>ch)
{
if(ch=='#') continue;
else if (ch=='(')
{
sta.push(t);
lev=1;//存在左子树
continue;
}
else if (ch==')')
{
sta.pop();
continue;
}
else if (ch==',')//存在右子树
{
lev=2;
continue;
}
int i=ch-'0';
t=new TreeNode(i);
if (T==NULL)//这里将树的根联系起来
{
T=t;
}
else {
if(lev==1&&!sta.empty())
{
TreeNode *p=sta.top();
p->lchild=t;
}
else if(!sta.empty())
{
TreeNode *p=sta.top();
p->rchild=t;
}
}
}
}
//先序遍历递归
void PreorderTraverse(TreeNode *root)
{
TreeNode *t=root;
if (t)
{
cout<<t->val;
PreorderTraverse(t->lchild);
PreorderTraverse(t->rchild);
}
}
//非递归先序遍历,先访问根,再依次访问左右子树
void PreorderTraverseNoneR(TreeNode *root)
{
TreeNode *t=root;
stack<TreeNode *>sta;
sta.push(t);
while (!sta.empty())//栈非空的情况下循环
{
t=sta.top();//每次访问的是栈顶元素
cout<<t->val;
if (t->lchild && t->rchild)//左右子树均存在,那么右子树先入栈,左子树先入栈先访问
{
sta.pop();
sta.push(t->rchild);
sta.push(t->lchild);
}
else if(t->lchild)//只有左子树,则左子树入栈
{
sta.pop();
sta.push(t->lchild);
}
else if (t->rchild)
{
sta.pop();
sta.push(t->rchild);
}
else sta.pop();//叶子节点,则父节点也要出栈
}
} a
//中序遍历
void InorderTraverse(TreeNode* root)
{
TreeNode *t=root;
if (t)
{
InorderTraverse(t->lchild);//先遍历左子树
cout<<t->val;//访问根节点
InorderTraverse(t->rchild);//再遍历右子树
}
}
//非递归仍然借用栈来做,但是注意最先输出的是最最左子树节点
void InorderTraverseNoR(TreeNode *root)
{
TreeNode *t=root;
stack<TreeNode*>sta;
sta.push(t);
while(!sta.empty())
{
t=sta.top();
//根节点的左子树被访问后,top又为根节点的时候,会再次被访问,如何解决?
//栈需要pop,但是可能又pop多了,所以干脆让NULL也入栈
while (t && !sta.empty() )
{
t=t->lchild;
sta.push(t);
}
sta.pop();
if (!sta.empty())//进行访问
{
t=sta.top();
sta.pop();
cout<<t->val;
sta.push(t->rchild);//右子树入栈
}
}
}
//后序非递归遍历,先左子树,后右子树,再根节点。
void PostOrderTraverse(TreeNode *root)
{
TreeNode *t=root;
if (t)
{
PostOrderTraverse(t->lchild);
PostOrderTraverse(t->rchild);
cout<<t->val;
}
}
/************************************************************************/
/* 后序遍历的难点在于:需要判断上次访问的节点是位于左子树,还是右子树。*/
/*若是位于左子树,则需跳过根节点,先进入右子树,再回头访问根节点;
若是位于右子树,则直接访问根节点。 */
/************************************************************************/
void PostOrderTraverseNoR(TreeNode *root)
{
TreeNode *t=root;
TreeNode *lastVisit=NULL;
stack<TreeNode*> sta;
sta.push(t);
//移到左子树
while (t)
{
sta.push(t);
t=t->lchild;
}//循环结束,t已经为空,且已经便利到左子树底端
while (!sta.empty())
{
t=sta.top();
sta.pop();
//根节点可以被访问的前提是没有右子树,或者右子树已经被访问
if(t->rchild==NULL || t->rchild==lastVisit)
{
cout<<t->val;
//修改最近被访问的节点
lastVisit=t;
}
else{
//根节点再次入栈
sta.push(t);
//进入右子树,且可肯定右子树一定不为空
t=t->rchild;
while (t)
{
sta.push(t);
t=t->lchild;
}
}
}
}
int main()
{
char ch;
TreeNode *root;
root=new TreeNode(-1);
//treeCreate(root);
treeCreateNoneR(root);
PreorderTraverse(root);
PreorderTraverseNoneR(root);
cout<<endl<<"In Order"<<" ";
InorderTraverse(root);
cout<<endl<<"In order none Re"<<" ";
InorderTraverseNoR(root);
cout<<"next "<<endl;
cout<<endl<<"Post order none Re"<<" ";
PostOrderTraverse(root);
PostOrderTraverseNoR(root);
}
最后一部分借鉴:
http://www.2cto.com/kf/201407/314705.html,多谢~
附上结果截图