Morris遍历【时间O(n),空间O(1)的二叉树遍历】

一般来说,二叉树有两种遍历方式,一种方式是递归遍历,一种是基于栈的非递归方式。

  • 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结点的右孩子指针指向rootroot结点迭代为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的右孩子指针为空,则让其指向curcur迭代为cur的左孩子结点
mostright的右孩子指针指向cur时,将其置空,cur迭代为cur的右孩子结点
4.重复过程2,3,直至curnull

代码实现

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

猜你喜欢

转载自blog.csdn.net/RealCoder/article/details/108732019
今日推荐