为什么学习Morris遍历
普通树的非递归遍历往往需要借助额外空间 比如:栈、队列
我们可以算出
额外空间O(h)
而Morris遍历
Morris空间O(1)
二者的时间复杂度都是O(n)
直觉上,认为它的复杂度是O(nlgn),因为找单个节点的前驱节点与树的高度有关。但事实上,寻找所有节点的前驱节点只需要O(n)时间。n个节点的二叉树中一共有n-1条边,整个过程中每条边最多只走2次,一次是为了定位到某个节点,另一次是为了寻找上面某个节点的前驱节点,如下图所示,其中红色是为了定位到某个节点,黑色线是为了找到前驱节点。所以复杂度为O(n)。
通过对比立见高下
Morris遍历流程
来到的当前节点记为Cur
<1>如果Cur无左孩子,Cur向右移动(Cur=Cur->right)
<2>如果Cur有左孩子,找到Cur左子树上最右的节点,记为mostright
(1)如果mostright的right指针指向空,让其指向Cur,Cur向左移动(Cur=Cur->left)
(2)如果mostright的right指针指向Cur,让其指向空,Cur向右移动(Cur=Cur->right)
附前序中序代码(后续相对麻烦很多,读者可自行实现)
void MorrisIn(BiTree T){
if(T==NULL)
return;
BiTree cur,mostRight;
cur = T;
mostRight = NULL;
while( cur != NULL ){
mostRight = cur->lchild;
if( mostRight != NULL){
while( mostRight->rchild != NULL && mostRight->rchild != cur)
mostRight = mostRight->rchild;
if( mostRight->rchild == NULL){
mostRight->rchild = cur;
cur = cur->lchild;
continue ;
}else{
mostRight->rchild = NULL;
}
}
printf("%c ",cur->data);
cur = cur->rchild;
}
printf("\n");
}
void MorrisPre(BiTree T){
if(T==NULL)
return;
BiTree cur,mostRight;
cur = T;
mostRight = NULL;
while( cur != NULL ){
mostRight = cur->lchild;
if( mostRight != NULL){
while( mostRight->rchild != NULL && mostRight->rchild != cur)
mostRight = mostRight->rchild;
if( mostRight->rchild == NULL){
printf("%c ",cur->data);
mostRight->rchild = cur;
cur = cur->lchild;
continue ;
}else{
mostRight->rchild = NULL;
}
}else{
printf("%c ",cur->data);
}
cur = cur->rchild;
}
printf("\n");
}