神级遍历二叉树的方法(Morris算法)上

在前面的文章中我们介绍了关于二叉树的三种遍历方式的递归和非递归的形式!时间复杂度为 O(n) ,空间复杂度也为 O(h) (h为树的高度),对于每个节点我们都要访问一遍,所以时间复杂度是没有办法优化了, 但是想一下,我们为什么要申请额外的数 据结构,这是因为二叉树这种结构,在处理完一个节点是时候很容易做到访问下层,但是很难做到访问上层,我们申请额外的数 据结构就是为了保存当前节点的父节点! 下面我们就介绍一种不使用额外数据结构的算法来实现二叉树的三种遍历,这个算法就是Morris算法!

Morris算法介绍

Morris算法在遍历的时候避免使用了栈结构,而是让下层到上层有指针,具体是通过底层节点指向NULL的空闲指针返回上层的某个节点,从而完成下层到上层的移动。我们知道二叉树有很多空闲的指针,比如某个人节点没有右孩子,我们称这种情况为空闲状态,Morris算法的遍历就是利用了这些 空闲的指针!
假设一颗二叉树如下图所示:
神级遍历二叉树的方法(Morris算法)上_第1张图片
下面以中序遍历为例说一下Morris算法的流程:
1. 假设当前子树的头结点为h,让h的左子树中的最右节点指向h,然后h的左子树继续步骤一的处理过程,直达遇到某一个节点没有左子树的时候记为node,进入步骤2
按照上图的说一下步骤一:h节点为4节点。通过步骤1让节点3的rson指向节点4,接下来以节点2为头的子树继续进入步骤1,然后让节点1的rson指向2,接下来以节点1为头的子树就没有左子树了,步骤1停止,节点1进入步骤2,这个时候二叉树的结构如下图:
神级遍历二叉树的方法(Morris算法)上_第2张图片
2. 从node节点开始通过每个节点的rson指针进行移动,并以此打印,假设移动到的节点为cur,对于每一个节点都判断cur节点的左子树中的最右节点是否指向cur。
— ①如果是。让cur节点的左子树种的最右节点的rson指针指向空,也就是把步骤1调整后的状态在调整回去,然后打印cur,继续通过cur的下一个节点,重复步骤2
— ②如果不是。以cur为头结点的子树重新回到步骤1操作
3. 步骤2最终移动到 null,结束整个过程!

中序遍历实现

通过上面的算法描述,中序遍历在打印某个节点的时候,一定是步骤2开始移动的过程中,而步骤2最初开始的位置一定是子树的最左节点,在通过right指针移动的过程中,我们能够发现要么是某个节点移动到右子树上,比如:节点2到节点3的移动,在发生这种情况的时候,左子树和根节点已经打印完毕,然后开始处理右子树的过程;要么是某个节点移动到某个上层节点,比如节点1向节点移动,发生这种情况的时候肯定是上层节点的左子树整体已经打印完毕,然后处理根节点(也就是上层节点)和 右子树的过程:

中序遍历实现代码

void morrisIn(Tree*  head)
{
    if(head==NULL) return ;
    Tree* cur = head;
    Tree* nxt = NULL;
    while(cur!=NULL) {
        nxt = cur->lson;
        if(nxt!=NULL) {
            while(nxt->rson!=NULL && nxt->rson!=cur) nxt = nxt->rson;
            if(nxt->rson==NULL) {
                nxt->rson = cur;
                cur=cur->lson;
                continue;
            } else {
                nxt->rson=NULL;
            }
        }
        cout<<cur->data<<" ";
        cur=cur->rson;
    }
    cout<<endl;
}

你可能感兴趣的:(ACM======start,ACM-数据结构)