Algorithm理解用例:二叉树的遍历

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/credolhcw/article/details/59046393

真的是已经滚瓜烂熟的算法 —— 前序、中序、后序遍历的几种实现

由于每个节点都要访问到,所以树的遍历时间复杂度必然大于O(n),所以优化方式主要是减少空间消耗。然而,我目前没有碰到过需要将空间复杂度控制在O(1)的情况,等有需要的时候再学习这种算法吧……


度为m(m>2)树的遍历

m树的遍历也有前序、中序、后序之分。前序和后序遍历很好理解,和二叉树的概念是一样的。

m树的中序遍历规则:先访问第一个子树,再访问根节点,然后访问其他的子树。

往往m树都会以孩子-兄弟表示法转化为对应的二叉树(BiTree)进行存储,而BiTree的中序遍历结果和m树的中序遍历结果是相同的。


二叉树的遍历

为了方便理解,我分前序、中序、后序三个部分进行复习,并且每个部分都使用递归、父节点指针、栈等三中方法实现。

为了灵活的调用各种Visit函数,我使用了成员函数指针进行函数调用。(参考博客《C++成员函数指针揭秘》:http://blog.csdn.net/icycode/article/details/6758636

Stack方法使用了STL中的stack库,如果懒得看头文件,可以看看这个博客:http://blog.csdn.net/wallwind/article/details/6858634

另,我自己写的测试程序类的定义放在文章的最后面。


二叉树前序遍历

1. 递归方法

int BiTree::biTreeTravelByRecursionPreOrder( BiTreeNode *btn, pFuncNoReturn pf )
{
    (this->*pf)(btn); // 以类成员函数指针的方式调用访问函数

    if( btn->lchild != NULL ) {
        // 左子树不为空,访问左子树
        this->biTreeTravelByRecursionPreOrder(btn->lchild,pf);
    }

    if( btn->rchild != NULL ) {
        // 右子树不为空,访问右子树
        this->biTreeTravelByRecursionPreOrder(btn->rchild,pf);
    }

    return 0;
}

 
2. 父节点指针方法

int BiTree::biTreeTravelByFatherPointerPreOrder(pFuncNoReturn pf )
{
    if( this->isEmpty() ) return 0; // 空树直接返回

    BiTreeNode *btn = this->_root; // 遍历指针

    /* 
     *  'i',记录已访问个数
     *  'while(i<this->_nodeNum)',全部节点都访问完,则跳出循环
     */
    int i = 0;
    while( i < this->_nodeNum ) { 
        /* 
         *  if的语义为“以btn为根的子树还未被访问”
         */
        if( !btn->getVisited() ) {
            while(1) {
                /* 访问根节点 */
                (this->*pf)(btn);
                btn->setVisited(true);
                i++;

                /* 访问左子树,直到左子树为空 */
                if( btn->lchild != NULL ) btn = btn->lchild;
                else break;
            }
        }

        /*
         *  前面的循环已经完成了根和左子树的访问
         *  若btn的右子树不为空且未被访问则访问右子树
         *  否则,即表示btn为根的子树已访问完毕,退回至父节点
         */
        if( btn->rchild != NULL && !btn->rchild->getVisited() ) btn = btn->rchild;
        else btn = btn->parent;
    }

    return 0;
}

 
3. Stack方法

int BiTree::biTreeTravelByStackPreOrder( pFuncNoReturn pf )
{
    BtnStack s;

    if( this->isEmpty() ) return 0;

    BiTreeNode *btn = this->_root;

    while(1) {
        while( btn != NULL ) {
            (this->*pf)(btn); // visit

            /* 存在右节点,则将右节点入栈 */
            if( btn->rchild != NULL ) s.push(btn->rchild);

            btn = btn->lchild;
        }

        if( s.empty() ) break; // 结束循环

        /* 根节点和左子树访问完毕后,将右节点出站继续访问 */
        btn = s.top();
        s.pop();
    }

    return 0;
}

二叉树中序遍历

1. 递归方法

int BiTree::biTreeTravelByRecursionInOrder( BiTreeNode *btn, pFuncNoReturn pf )
{   
    if( btn->lchild != NULL ) {
        this->biTreeTravelByRecursionInOrder(btn->lchild,pf);
    }

    (this->*pf)(btn);

    if( btn->rchild != NULL ) {
        this->biTreeTravelByRecursionInOrder(btn->rchild,pf);
    }

    return 0;
}

 
2. 父节点指针方法

int BiTree::biTreeTravelByFatherPointerInOrder(pFuncNoReturn pf )
{
    if( this->isEmpty() ) return 0;

    BiTreeNode *btn = this->_root;

    int i = 0;
    while( i < this->_nodeNum ) {
        /* 中序遍历先访问左子树,所以先将btn指向当前子树的最左端 */
        while( btn->lchild != NULL && !btn->lchild->getVisited() ) btn = btn->lchild;

        /* 
         *  由于已经将btn移至当前子树的最左端
         *  所以相当于访问了子树btn的根节点
         */
        if( !btn->getVisited() ) {
            (this->*pf)(btn);
            btn->setVisited(true);
            i++;
        }

        /* 
         *  若子树btn存在右节点,则将btn指向右子树的根
         *  否则返回至父节点         
         */
        if( btn->rchild != NULL && !btn->rchild->getVisited() ) btn = btn->rchild;
        else btn = btn->parent;
    }

    return 0;
}

 
3. Stack方法

int BiTree::biTreeTravelByStackInOrder( pFuncNoReturn pf )
{
    BtnStack s;

    if( this->isEmpty() ) return 0;

    BiTreeNode *btn = this->_root;

    while(1) {
        while( btn != NULL ) {
            s.push(btn); // 根节点入栈
            btn = btn->lchild;
        }
        if( s.empty() ) break;

        /* 访问栈顶元素 */
        (this->*pf)(s.top()); // visit

        /* 若右子树为空,则正好不会进入上面的while循环,从而继续出站操作 */
        btn = s.top()->rchild;
        s.pop();
    }

    return 0;
}

二叉树后序遍历

1. 递归方法

int BiTree::biTreeTravelByRecursionPostOrder( BiTreeNode *btn, pFuncNoReturn pf )
{
    if( btn->lchild != NULL ) {
        this->biTreeTravelByRecursionPostOrder(btn->lchild,pf);
    }

    if( btn->rchild != NULL ) {
        this->biTreeTravelByRecursionPostOrder(btn->rchild,pf);
    }

    (this->*pf)(btn);

    return 0;
}

2. 父节点指针方法

int BiTree::biTreeTravelByFatherPointerPostOrder(pFuncNoReturn pf )
{
    if( this->isEmpty() ) return 0;

    BiTreeNode *btn = this->_root;

    int i = 0;
    while( i < this->_nodeNum ) {
        while( btn->lchild != NULL && !btn->lchild->getVisited() ) btn = btn->lchild;

        if( btn->rchild != NULL && !btn->rchild->getVisited() ) btn = btn->rchild;
        else {
            /* 访问当前树的最左端或者最下端的节点 */
            (this->*pf)(btn);
            btn->setVisited(true);
            i++;
            btn = btn->parent;
        }
    }

    return 0;
}

 
3. Stack方法

int BiTree::biTreeTravelByStackPostOrder( pFuncNoReturn pf )
{
    BtnStack s;

    if( this->isEmpty() ) return 0;

    BiTreeNode *btn = this->_root;

    while(1) {
        /*
         *  向左遍历
         *  将沿路的未访问节点都入栈
         *  最左端(未访问)的节点不入栈
         */
        while( btn->lchild != NULL && !btn->lchild->getVisited() ) {
            s.push(btn);
            btn = btn->lchild;
        }

        /*
         *  btn指向当前树的最左端(未访问)节点
         *  若存在右节点,且未访问,则将btn入栈,将btn指向其右子树根节点,继续循环
         *  否则,访问btn
         */
        if( btn->rchild != NULL && !btn->rchild->getVisited() ) {
            s.push(btn);
            btn = btn->rchild;
        }
        else {
            (this->*pf)(btn); // visit
            btn->setVisited(true);

            if( s.empty() ) break;
            btn = s.top();
            s.pop();
        }
    }

    return 0;
}

相关定义

二叉树的节点类,BiTreeNode

class BiTreeNode
{
private:
    char _data;
    bool _visited; // "true"表示已访问

public:
    BiTreeNode *lchild;
    BiTreeNode *rchild;
    BiTreeNode *parent;

public:
    BiTreeNode( char data ) {
        this->_data = data;
        this->_visited = false;
        this->lchild = NULL;
        this->rchild = NULL;
        this->parent = NULL;
    }

public:
    char getData() { return this->_data; }
    bool getVisited() { return this->_visited; }
    void setVisited( bool visited ) { this->_visited = visited; }
    void setData( int data ) { this->_data = data; }
};

 
为Stack定义别名BtnStack(Btn,即 Binary tree node):

typedef std::stack<BiTreeNode*> BtnStack;

 
二叉树类定义(BiTree),由于不想些一大堆的注释,所以我吧函数名写的有点长( ̄ε  ̄) :

// 为函数指针pFuncNoReturn取别名,所以需要先声明类BiTree
class BiTree; 
typedef void (BiTree::*pFuncNoReturn)( BiTreeNode *btn );

class BiTree  
{
private:
    BiTreeNode *_root; // 二叉树根节点
    int _nodeNum; // 二叉树的节点数

public:
    BiTreeNode *buff; // 存函数biSearch返回的位置,和本文的内容没关系
    BiTree() {
        this->_root = NULL;
        this->buff = NULL;
        this->_nodeNum = 0;
    }

public:
    bool isEmpty() {
        if( this->_nodeNum <= 0 ) return true;
        return false;
    }


public:
    bool biSearch( char target ); // 二分检索target,本文就当做createBiTree用
    void biRenew() { 
        // 用遍历的方式置所有节点的_visited属性为“未访问状态”
        this->biTreeTravelByRecursionPreOrder(this->_root,this->renewNodeData); 
    }

public:
    /* pFuncNoReturn */
    void printNodeData( BiTreeNode *btn ) { std::cout << btn->getData(); }
    void renewNodeData( BiTreeNode *btn ) { btn->setVisited(false); }

private:
    /* Travel by recursion type */
    int biTreeTravelByRecursionPreOrder( BiTreeNode *btn, pFuncNoReturn pf );
    int biTreeTravelByRecursionInOrder( BiTreeNode *btn, pFuncNoReturn pf );
    int biTreeTravelByRecursionPostOrder( BiTreeNode *btn, pFuncNoReturn pf );

    /* Travel by Pointer */
    int biTreeTravelByFatherPointerPreOrder( pFuncNoReturn pf );
    int biTreeTravelByFatherPointerInOrder( pFuncNoReturn pf );
    int biTreeTravelByFatherPointerPostOrder( pFuncNoReturn pf );

    /* Travel by Stack */
    int biTreeTravelByStackPreOrder( pFuncNoReturn pf );
    int biTreeTravelByStackInOrder( pFuncNoReturn pf );
    int biTreeTravelByStackPostOrder( pFuncNoReturn pf );
}

猜你喜欢

转载自blog.csdn.net/credolhcw/article/details/59046393