补上一篇
基于上面的3个基本操作,下面是两种方式的遍历树
1,普通的非递归遍历二叉树(补充)
2,线索化的树上3种遍历方式
非递归遍历:
a,中序遍历
思路:
只要一直把树中最左的节点作为第一个节点输出即可,因此需要把搜索最左节点路径上的根节点存储起来,存储结构用栈(用于回溯) 一个临时节点temp(初始值为根节点),如果temp"非NULL"且存在左节点(非左线索), 则把左节点压入栈中(包括temp) 并把temp更新为temp的左节点,知道temp不存在 左节点(压入栈中);此时输出栈顶,同时栈顶节点如果存在右子树的,temp更新为 此节点的右节点,如果没有右子树(则"置temp为null")
代码:
template<class T>
void ttree<T>::iter_inorder( ){
cout<<"非递归中序遍历:"<<endl;
if(root->ltd) return;
tree temp=root->lcd;
stack<tree, vector<tree > > st;
for(;;){
for(; temp && !temp->ltd;temp=temp->lcd)
st.push(temp);
if(temp)st.push(temp);
if(st.empty())break;
temp=st.top();
st.pop();
cout<<temp->data<<" ";
if(!temp->rtd) temp=temp->rcd;
else temp=NULL;
}
}
b.前序遍历(最简单)
思路:
跟中序遍历的思路差不多,建立栈存储当前遍历的左节点及右节点
简单,不用临时节点temp
代码:
template<class T>
void ttree<T>::iter_preorder(){
cout<<"非递归前序遍历:"<<endl;
stack<tree, vector<tree> >st;
if(root->ltd) return;
tree temp=root->lcd;
st.push(temp);
for(;;){
temp=st.top(); st.pop();
cout<<temp->data<<" ";
if(!temp->rtd) st.push(temp->rcd);
if(!temp->ltd) st.push(temp->lcd);
if(st.empty()) break;
}
}
c.后序遍历
思路:
和中序遍历很接近,两者都是通过栈结构实现;跟中序遍历不同:前者只要让左节点先入栈;而后者是要保证左右节点都在根节点之前入栈;根据后序遍历的特点,后序遍历序列中根节点root一定是尾随其右子树根节点之后,因此可以设置一个标志前一个输出的节点,然后判断栈顶节点的右节点是否为标志节点->是:输出当前节点,置temp为NULL;否:栈顶节点右子树还没入栈,置temp为栈顶节点右子树的根节点
代码:
template<class T>
void ttree<T>::iter_postorder(){
cout<<"非递归后序遍历:"<<endl;
stack<tree, vector<tree> > st;
if(root->ltd) return;
tree temp=root->lcd;
tree r=NULL; // tag the right tree
while(temp || !st.empty())
{
if(temp){
st.push(temp);
temp=temp->ltd? NULL:temp->lcd;
}
else{
temp=st.top();
if(!temp->rtd && r!=temp->rcd){
temp=temp->rcd;
}
else {
st.pop(); cout<<temp->data<<" ";
r=temp; temp=NULL;
}
}
}
}
2,线索化的三种遍历:
a.中序遍历
思路:
在实现了求节点中序遍历后续节点的函数,中序遍历就基本上完成了,只要从根节点开始不断求后续节点即可
代码:
template<class T>
void ttree<T>::tiorder()
{
printf("线索中序遍历\n");
tree temp=root;
for(;;){
temp=insucc(temp);
if(temp==root) break;
printf("%d ",temp->data);
}
}
b.前序遍历
思路:
前序遍历的遍历顺序是根 左子树 右子树;直接利用左链域可以遍历节点左子树,而从左子树返回到对应根节点的右子树,上面的非递归是用栈存储的,而在线索化的二叉树,可利用节点的后续节点找到左子树的根节点,而该根节点的右链域可以遍历其右子树由临时节点temp从右线索不断往会就可以找到根节点
代码:
template <class T>
void ttree<T>::tpreorder()
{
printf("线索前序遍历\n");
if(root->ltd) return;
tree temp=root->lcd;
for(;temp;){
cout<<temp->data<<" ";
if(!temp->ltd) {
temp=temp->lcd;
continue;
}
if(!temp->rtd){
temp=temp->rcd;
continue;
}
if(temp->rcd==root) break;
while(temp->rtd)
temp=temp->rcd; //利用线索不断回溯
temp=temp->rcd; //所求的右子树
}
}
c. 后序遍历
思路:
我是通过求出一个节点在后序遍历中的后续节点,通过实现这个操作, 线索的后序遍历就跟线索的线索中序遍历的实现代码几乎一样,节点Node的后续节点(后序遍历):
先求出Node的父节点fNode,如果Node是fNode的右子树的根节点,则Node的后续节点就是fNode;否则Node的后续节点就是fNode的右子树的最左下节点
代码:
//后序遍历中的后继节点函数
template <class T>
tnode<T>* ttree<T>::lnsucc(tree& node)
{
tree pnode=fPnode(node);
if(pnode==node) return root;
if(pnode->rtd || pnode->rcd==node) return pnode;
else { //pnode 肯定有右子树
pnode=pnode->rcd;
while(!pnode->ltd)
pnode=pnode->lcd;
return pnode;
}
}
template <class T>
void ttree<T>::tposorder()
{
cout<<"线索后序遍历:"<<endl;
if(root->ltd) return;
tree temp=insucc(root);
for(;temp!=root;) {
cout<<temp->data<<" ";
temp=lnsucc(temp);
}
}