Morris算法
Morris算法是一种可以用于遍历二叉树的算法,在刷题的时候发现实现二叉树前中后和层序遍历除了递归和迭代外,还有一种morris算法,可以实现二叉树的前中(后)遍历。主要使用回溯,但是他的回溯是回溯左节点不为空的节点两次(也正是因为最多只能访问某个节点两次,所以没有办法通过直接输出数值实现后序遍历),理解之后发现原来之前就见识过这种算法,只是当时不知道这种方法是一种算法,当时理解的还挺艰难的hhhhh
我是看这一篇bolg理解的,我称之为最强Morris讲解
这篇blog讲解了Morris算法的步骤,主要还是需要自己跟着二叉树走几遍程序就懂了
这是我跟着走的步骤,二叉树是上面给出的链接给的例子,并写出了Java的实现代码
C++实现Morris算法
这里给出C++版本的这个树的Morrios遍历
void Morris(Treenode* root)
{
Treenode* cur = root;
while (cur != NULL)
{
if (cur->left == NULL)
{
cur = cur->right;
}
else//左子树不为空
{
Treenode* next = cur->left;
while (next->right != NULL && next->right != cur) next = next->right;
//下面的这两步保证了next的右子树总是不为空的,就算是空的,他也会被赋值为cur
if (next->right == NULL)
{
next->right = cur;
cur = cur->left;
}
else//如果到了这一步就说明next->right是cur,那么就说明cur已经被回溯过了,cur的左边都被走过了,那么就要将cur这个指针移动到他的右子树上了
{
cout << cur->val << endl;
cur = cur->right;
next->right = NULL;
}
}
}
}
写出来之后我开始思考怎么使用这个算法实现二叉树的前中后遍历,但是奈何我才疏学浅,没有想出来,在看了之后的讲解之后,直呼妙哇,因为我并没有重视使用递归和迭代实现遍历二叉树时候的执行顺序和访问/经过某个节点的次数,所以导致没有想出解法。再次感叹博客作者写出这篇文章!
Morris前序遍历
使用Morris算法实现的二叉树前序遍历如下:
void Preorder_Morris(Treenode* root)
{
cout << "PreOrder:";
Treenode* cur = root;
while (cur != NULL)
{
if (cur->left == NULL)
{
cout << cur->val << " ";
cur = cur->right;
}
else
{
Treenode* next = cur->left;
while (next->right != NULL && next->right != cur) next = next->right;
if (next->right == NULL)
{
next->right = cur;
cout << cur->val << " ";
cur = cur->left;
}
else
{
cur = cur->right;
next->right = NULL;
}
}
}
}
Morris中序遍历
使用Morris算法实现的二叉树中序遍历如下:
void Midorder_Morris(Treenode* root)
{
cout << "Mid_Order:";
Treenode* cur = root;
while (cur != NULL)
{
if (cur->left == NULL)
{
cout << cur->val << " ";
cur = cur->right;
}
else
{
Treenode* next = cur->left;
while (next->right != NULL && next->right != cur) next = next->right;
if (next->right == NULL)
{
next->right = cur;
cur = cur->left;
}
else
{
cout << cur->val << " ";
cur = cur->right;
next->right = NULL;
}
}
}
}
关于Morris后序遍历
前文说过,因为后序遍历输出根节点的时候是在第三次访问根节点的时候,但是Morris最多访问一个节点的次数是2,也就是说,没有办法直接使用Morris算法实现后序遍历。但是可以使用数组等数据结构存储前序遍历的结果,反转/倒序遍历数组之后实现后序遍历,这里就不过多叙述了,详细可以看我上面推荐的那篇博客
源代码
#include <iostream>
#include<vector>
using namespace std;
class Treenode
{
public:
int val;
Treenode* left;
Treenode* right;
Treenode(int val):val(val),left(NULL),right(NULL){}
Treenode():val(0), left(NULL), right(NULL) {}
};
void Morris(Treenode* root)
{
cout << "This is Morris Algorithm" << endl;
Treenode* cur = root;
while (cur != NULL)
{
if (cur->left == NULL)
{
cur = cur->right;
}
else//左子树不为空
{
Treenode* next = cur->left;
while (next->right != NULL && next->right != cur) next = next->right;
//下面的这两步保证了next的右子树总是不为空的,就算是空的,他也会被赋值为cur
if (next->right == NULL)
{
next->right = cur;
cur = cur->left;
}
else//如果到了这一步就说明next->right是cur,那么就说明cur已经被回溯过了,cur的左边都被走过了,那么就要将cur这个指针移动到他的右子树上了
{
cout << cur->val << endl;
cur = cur->right;
next->right = NULL;
}
}
}
}
void Preorder_Morris(Treenode* root)
{
cout << "PreOrder:";
Treenode* cur = root;
while (cur != NULL)
{
if (cur->left == NULL)
{
cout << cur->val << " ";
cur = cur->right;
}
else
{
Treenode* next = cur->left;
while (next->right != NULL && next->right != cur) next = next->right;
if (next->right == NULL)
{
next->right = cur;
cout << cur->val << " ";
cur = cur->left;
}
else
{
cur = cur->right;
next->right = NULL;
}
}
}
}
void Midorder_Morris(Treenode* root)
{
cout << "Mid_Order:";
Treenode* cur = root;
while (cur != NULL)
{
if (cur->left == NULL)
{
cout << cur->val << " ";
cur = cur->right;
}
else
{
Treenode* next = cur->left;
while (next->right != NULL && next->right != cur) next = next->right;
if (next->right == NULL)
{
next->right = cur;
cur = cur->left;
}
else
{
cout << cur->val << " ";
cur = cur->right;
next->right = NULL;
}
}
}
}
int main()
{
//构造二叉树
Treenode* root = new Treenode(1);
Treenode* b = new Treenode(2);
Treenode* c = new Treenode(3);
Treenode* d = new Treenode(4);
Treenode* e = new Treenode(5);
Treenode* f = new Treenode(6);
Treenode* g = new Treenode(7);
Treenode* h = new Treenode(8);
root->left = b;
root->right = c;
b->left = d;
b->right = e;
c->left = f;
c->right = g;
e->right = h;
//Morris(root);
//Preorder_Morris(root);
//Midorder_Morris(root);
return 0;
}