一般来说,二叉树有两种遍历方式,一种方式是递归遍历,一种是基于栈的非递归方式。
- 1°递归遍历,有时间复杂度 O ( n ) O(n) O(n),空间复杂度平均 O ( l o g n ) O(logn) O(logn),最坏情况下 O ( n ) O(n) O(n)
- 2°基于栈的非递归遍历,有时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( h ) O(h) O(h)
而有一种巧妙的方法可以在时间复杂度为O(n),空间复杂度为O(1)的情况下实现二叉树遍历。这种方法由 J. H. Morris
在 1979 年的论文「Traversing Binary Trees Simply and Cheaply」
中首次提出,因此被称为 Morris
遍历。
一、先序遍历
算法
1.如果跟结点root
的左孩子为空,则打印root
结点,root
结点迭代为root
的右孩子结点(所以函数参数不要为了图快而引用传递)
否则,定义cur
结点为根结点root
左子树的最右下角的结点,接下来又有两种情况:
①cur
结点的右孩子指针已经指向root
时,将该指针置空,root
结点迭代为root
结点的右孩子
②cur
结点的右孩子指针为空时,打印root
结点值,将cur
结点的右孩子指针指向root
,root
结点迭代为root
结点的左孩子
2.重复过程1直至root
结点迭代为NULL
代码实现
// Preorder traversal without recursion and without stack
void morrisTraversalPreorder(node* root)
{
while (root)
{
// If left child is null, print the current node data. Move to
// right child.
if (root->left == NULL)
{
cout<<root->data<<" ";
root = root->right;
}
else
{
// Find inorder predecessor
node* current = root->left;
while (current->right && current->right != root)
current = current->right;
// If the right child of inorder predecessor already points to
// this node
if (current->right == root)
{
current->right = NULL;
root = root->right;
}
// If right child doesn't point to this node, then print this
// node and make right child point to this node
else
{
cout<<root->data<<" ";
current->right = root;
root = root->left;
}
}
}
}
二、中序遍历
算法
1.初始化cur
结点为root
2.当cur
不空时,检查其是否有左孩子结点
3.如果cur
无左孩子,打印cur
,并将其cur
迭代为cur
的右孩子结点
如果cur
有左孩子,找到cur
左子树的最右下方的叶子结点,定义为mostright
此时又有两种情况
①mostright
的右孩子指针为空,则让其指向cur
,cur
迭代为cur
的左孩子结点
②mostright
的右孩子指针指向cur
时,将其置空,cur
迭代为cur
的右孩子结点
4.重复过程2,3,直至cur
为null
代码实现
void Morris( Node* root)
{
Node *cur, *mostright;
if (root == NULL) return;
cur = root;
while (cur != NULL) {
if (cur->left_node == NULL) {
cout << cur->data << endl;
cur = cur->right_node;
}
else {
/* Find the previous of cur */
mostright = cur->left_node;
while (mostright->right_node != NULL && mostright->right_node != cur)
mostright = mostright->right_node;
/* Make cur as the right child of itsprevious */
if (mostright->right_node == NULL) {
mostright->right_node = cur;
cur = cur->left_node;
}
/* fix the right child of previous */
else {
mostright->right_node = NULL;
cout << cur->data << endl;
cur = cur->right_node;
}
}
}
}
三、后序遍历
算法
后序遍历较为复杂,这里直接贴出算法原文。
代码实现
暂无
参考:geeksforgeeks论坛与edpresso