非递归且不使用栈或者线索也可以遍历树

来源:互联网 发布:java 字符串格式化 编辑:程序博客网 时间:2024/06/05 06:48

以前总是不太喜欢把所学的知识记录下来,但是最近觉得,把装在脑袋里的一些东西释放出来,大脑能够更有效地去吸收新的营养。于是这次就决定试上一试,看看效果如何。

一般来说,我们对树的遍历要么是使用直观的递归遍历,要么是使用栈Stack,通过栈的操作实现对访问顺序的控制,前面这两种都是隐示或显示地使用栈来存储当前没有处理完的节点信息。在递归函数中,使用了运行时栈。在非递归的算法变体中,显式定义和使用了由用户维护的栈。程序要花费额外的时间来维护栈,还要为栈留出更多的空间。在最坏情况下,当树以不利于程序运行的方式“倾斜”时,栈将保存权中几乎所有节点的信息,这对于很大的树来说是一个严重的问题。

于是就想到,能不能既不使用栈或者线索也可以遍历树呢?事实上有许多这样的算法,它们都在遍历过程中对树进行了临时的修改,这些修改包括给某些指针重新赋新值,当然在完成遍历后,这些树结构需要恢复。

Joseph M.Morris 开发了一个精致的算法,它应用于中序遍历树:

template<class T>

void BST<T>::MorrisInorder(){   //中序遍历

    BSTNode<T> *p = root, *tmp;

    while (p != NULL){

        if (p->left == NULL){

            visit(p);

            p = p->right;

        }

        else {

            tmp = p->left;

            while (tmp->right != NULL && tmp->right != p){  

                //go to the rightmost nodeof the left subtree

                //or to the temporary parent of p;

                tmp = tmp->right;

            }

            if (tmp->right == NULL){    

                tmp->right = p;//if 'true' rightmost node was reached,

                p = p->left;    //make it a temporary parent of the current root

            }

            else {//else a temporary parent hasbeen found;

            //visit node p and then cut the rigth pointer of the current parent,

            //whereby it ceases to be a parent;

                visit(p);

                tmp->right = NULL;

                p = p->right;

            }

        }

    }

}

       很容易从中序遍历算法中得到前序遍历算法,只需要将 visit() 从内层的 else 子句移到内层的的 if 子句就可以了。这样,节点就在树转换之前访问:

template<class T>

void BST<T>::MorrisPreorder(){  //前序遍历

    BSTNode<T> *p = root, *tmp;

    while (p != NULL){

        if (p->left == NULL){

            visit(p);

            p = p->right;

        }

        else {

            tmp = p->left;

            while (tmp->right != NULL && tmp->right != p){

                tmp = tmp->right;

            }

            if (tmp->right == NULL){    

                visit(p);

                tmp->right = p;

                p = p->left;

            }

            else {

                tmp->right = NULL;

                p = p->right;

            }

        }

    }

}

 

 

后序遍历也可以从中序遍历算法中得到:首先创建一个哑节点,它的左子树是正在处理的树,它的右子树是空。接着,这棵临时扩展的树就成为遍历的主体,与中序遍历的树类似,但在内层的 else 子句中,在找到临时的父节点后,p->left(已包含)和 p已排除)之间、在修改的树中扩展到右侧的节点是以相反的顺序处理的。而这种逆序处理依赖于在向下扫描节点时,顺便翻转节点链的右指针,以指向节点的的父节点。然后,在找到临时的父节点  p 时便向上扫描节点链,访问每个节点,并将右指针恢复为原来的值:

template<class T>

void BST<T>::MorrisPostorder(){ //后序遍历

    BSTNode<T> Root, *p = &Root, *tmp, *p1, *p2;

    Root->left = root;  //哑节点的左子树是正在处理的树

    while (p != NULL){

        if (p->left == NULL){

            p = p->right;

        }

        else {

            tmp = p->left;

            while (tmp->right != NULL && tmp->right != p){  

                tmp = tmp->right;

            }

            if (tmp->right == NULL){

                tmp->right = p;

                p = p->left;

            }

            else {

                tmp->right = NULL;

                tmp = p->left;  p1 = tmp->right;

                while (p1 != NULL){

                    p2 = p1->right;

                    p1->right = tmp;

                    tmp = p1;

                    p1 = p2;

                }

                while (tmp != p->left){

                    visit(tmp);

                    p1 = tmp->right;

                    tmp->right = p2;

                    p2 = tmp;

                    tmp = p1;

                }

                visit(tmp);

                p = p->right;

            }

        }

    }

}

原创粉丝点击