关于Morris遍历

对于树的遍历,无论是递归还是迭代,都是需要额外的空间,由系统记录访问节点压栈,或者自己压栈。而Morris遍历,通过利用节点中的空闲指针,空间复杂度可以做到O(1),且时间复杂度依旧不变O(N).

Morris遍历规则:

1.若该节点cur有左树,则找到左树最右节点Morrisright
(1)若Morrisright的右指针指向NULL,则让其指向cur,cur左移动。
(2)若Morris右指针指向cur,则让该右指针指向空,cur右移动。
2.若无左树,cur右移。
直到cur为空,整棵树遍历完毕,可得Morris序(cur所在节点轨迹)。1 2 4 2 5 1 3 6 3 7
在这里插入图片描述
有Morris序可发现规律
1.无左树的结点,只会出现1次,如结点4,5,6,7。
2.有左树的结点会出现两次,且在其第二次出现期间会把左树结点便利完成。如结点1,2,3.

void morri(node *root)
{
    if(root==NULL)
        return ;
    node *cur = root;
    node *morriright;
    while(cur)
    {
        if(cur->left!=NULL)//有左树
        {
            morriright = cur->left;
            while(morriright->right&&morriright->right!=cur)//找到左子树最右节点
            {
                morriright = morriright->right;
            }

            if(morriright->right==NULL)//为空
            {
                morriright->right = cur;//把其右指针指向当前cur节点
                cur = cur->left;//cur往左移
            }else//Morris节点不为空
            {
                morriright->right = NULL;//还原
                cur = cur->right;//cur右移
            }
        }else{
            cur = cur->right;//无左树则往右移
        }
    }

}

Morris下的前中后序遍历

1.前序与中序
由上面规律知,前序遍历即只出现一次的节点在第一次出现即打印,出现两次的结点也均在第一次打印。而中序遍历不同在于出现两次的结点在第二次出现才打印。

void morri(node *root)
{
    if(root==NULL)
        return ;
    node *cur = root;
    node *morriright;
    while(cur)
    {
        if(cur->left!=NULL)       {
            morriright = cur->left;
            while(morriright->right&&morriright->right!=cur)
            {
                morriright = morriright->right;
            }
			
            if(morriright->right==NULL)//第一次出现
            {
            	//cout<<cur->val<<" ";
                morriright->right = cur;
                cur = cur->left;
            }else//第二次出现
            {
            	cout<<cur->val<<" ";
                morriright->right = NULL;
                cur = cur->right;
            }
        }else{
        	cout<<cur->val<<" ";
            cur = cur->right;//无左树,只会出新一次的结点在此
        }
    }

}

2.后序
后序遍历在迭代中也是相对复杂的。如下图我们只4 5 2 6 7 3 1为后序遍历,则可知,需要倒序打印红线上的结点。对于倒序,则参考单链表倒序,打印完后再倒回来。
在这里插入图片描述

node* reverse(node *head)
{
    node* pre = NULL;
    node* next = NULL;
    while(head)
    {
        next = head->right;
        head->right = pre;
        pre = head;
        head = next;
    }
    return pre;
}

void printfreverse(node *head)
{

    node *cur = reverse(head);
    node *tmp = cur;
    while(tmp)
    {
        cout<<tmp->val<<" ";
        tmp = tmp->right;
    }
    reverse(cur);
}

void morri(node *root)
{
    if(root==NULL)
        return ;
    node *cur = root;
    node *morriright;
    while(cur)
    {
        if(cur->left!=NULL)
        {
            morriright = cur->left;
            while(morriright->right&&morriright->right!=cur)
            {
                morriright = morriright->right;
            }
            if(morriright->right==NULL)
            {
                morriright->right = cur;
                cur = cur->left;
            }else
            {
                morriright->right = NULL;
                printfreverse(cur->left);
                cur = cur->right;
            }
        }else{
            cur = cur->right;
        }
    }
   printfreverse(root);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值