二叉树的Morris遍历,是一种无堆栈,无递归,空间复杂度O(1)的高效遍历算法,这个算法在遍历的过程中需要改变二叉树的叶子节点的又指针域,即原来为NULL的要发生变化,但是遍历完成以后,过程中的变化又会恢复,所以遍历前,遍历后,二叉树不发生任何改变。
先给出伪代码,网上的
MorrisInOrder():
while 没有结束
如果当前节点没有左后代
访问该节点
转向右节点
否则
找到左后代的最右节点,且使最右节点的右指针指向当前节点
转向左后代节点
现在先考虑中序遍历,左,中,右
即整个代码分为两个部分,
1:对于没有左后代的节点:
说明应该访问该节点,然后当前节点指向其右孩子
2:对于有左后代的节点:
当前节点应该继续指向其左孩子,说到这,前面的逻辑关系跟其他递归形式的遍历一样,Morris这时候,是将其左孩子的最右节点指向自己,即:左之最右指向自己
执行过程中,在当前节点依次向左节点移动的过程中,一层一层的将左子树的最右指向自己
为啥要这样做呢?
这是为了在当前节点到达叶子节点的时候能够回去,前面2做的工作是给后面的铺路,确保当前节点跑到叶子节点的时候能回朔;这样就能理解1为啥左子树为空的时候可以指向右子树,这是因为前面已经铺好了道路
还有一个:就是过河拆桥,我前面铺了路,后面的人走完了以后要帮我把那条路清除,就是过河拆桥,这样做不是为了不改变二叉树结构,而是必须这么做,如果不这么做,那么二叉树首先逻辑混乱,其次我前面的左之最后指向自己就没法正确执行,因为最右指向了自己,我如果再这么执行,那么这个最右不知跳到哪儿去了
过河拆桥的细节体现在
while(pTemp->right!=NULL&&pTemp->right!=pCur)这个&&以后的语句上
代码如下:
void morris(Node *pRoot)
{
Node *pCur=pRoot;
Node *pTemp;
while(pCur)
{
if (pCur->left==NULL)
{
cout<<pCur->nData<<'\t';
pCur=pCur->right;
}
else
{
pTemp=pCur->left;
while(pTemp->right!=NULL&&pTemp->right!=pCur)
{
pTemp=pTemp->right;
}
if (pTemp->right=NULL)
//这里执行左之最右指向自己,然后当前节点向左移,这里是第一次
{
pTemp->right=pCur;
pCur=pCur->left;
}
else
//下面的工作是拆桥,对于pCur来说已经不能向左走了,因为我刚从左边过来,所以向右走
{
pTemp->right=NULL;
pCur=pCur->right;
}
}
}
}