二叉树不是线性结构,因此没有全局的顺序。而遍历算法将在一个局部给二叉树添加一个次序,从而间接地定义出全局的顺序。
先序遍历
考察一个二叉树的局部的一个节点v,则v的左子树与右子树(假设它们都存在)规定一个次序,先序遍历下,先访问节点v,然后深入左子树,再深入右子树。根据上述定义,实现递归版的先序遍历算法十分简单。
template<typename T,typename VST>void travPre_R(BinNodePosi(T) v,VST& visit){
if(!v) return;
visit(v->data);
travPre_R(v->lc,visit);
travPre_R(v->rc,visit);
}
而深入讨论该算法,该算法属于尾递归,采用消除尾递归的形式可以节省常系数意义上的时间复杂度和空间复杂度,当然,迭代版的先序遍历算法在总体意义上还是需要线性时间正比树高。
迭代思路:从整个算法来看,就是先访问当前节点,然后转入左子树,再转到右子树。可以不以引入一个栈结构,将遇到的所有节点压入栈,左子树优先出栈所以要后入栈。
template<typename T,typename VST>void travPre_R1(BinNodePosi(T) v,VST& visit){
Stack<BinNodePosi(T)> S;
if(v) S.push(v);//遇到节点全部压入栈
while(!S.empty()){//栈不空
v = S.pop(); //依次弹出
visit(v->data);
if(HasRChild(*v)) S.push(v->rc);//右子树优先
if(HasLChild(*v)) S.push(v->lc);//左子树次之
}
}
这是尾递归消除,因此不适用于一般的情况。
重新换一种角度来审视先序遍历,可不可以认为是这种情况,总是先访问所有的左子树,然后才开始访问右子树。
也就是说:
- 自上而下遍历所有左子树。
- 自下而上对所有右子树进行访问。
template<typename T,typename VST>void travPre_R2(BinNodePosi(T) v,VST& visit){
Stack<BinNodePosi(T)> S;
while(1){
visitAlongLeftBranch(x,visit,S);
if(S.empty()) break;
x = S.pop();
}
}
template<typename T,typename VST>void visitAlongLeftBranch(BinNodePosi(T) x,VST& visit, Stack<BinNodePosi(T)> &S){
while(x){
visit(x->data);
S.push(x->rc);
x = x->lc;
}
}