二叉树的Morris遍历

文章介绍了二叉树的Morris遍历方法,它是一种通过循环而非递归实现的中序遍历,具有较低的空间复杂度。Morris遍历基于线索二叉树的概念,通过修改二叉树结构,在遍历过程中无需额外存储节点。文章通过示例展示了如何建立线索二叉树以及如何进行Morris遍历,同时指出了在遍历过程中的注意事项,以避免错误,并讨论了如何在遍历后恢复原二叉树结构。

不同于通过递归实现的中序遍历,二叉树还存在一种通过循环实现的Morris遍历,,这种遍历方式从结果上来看与中序遍历相同,而由于是用循环代替了递归,空间复杂度会从O(H)(H为二叉树的高度)变为O(1)

线索二叉树

二叉树的Morris遍历,是基于线索二叉树来做的

而线索二叉树,简单来讲,就是将左节点不为空的节点的左子树的最右节点的右节点指向该节点

例如这样一个二叉树

其中 4 的左节点为 1 ,而 1 的最右节点为 3 ,因此我们需要将3的右节点指向 1 (虚线表示)

 

 因此,将该树表示为线索二叉树为

 


Morris遍历

在中序遍历中,我们的遍历顺序为左节点 - 根节点 - 右节点

首先,想要得到左节点,我们可以直接得到(root=root->left)

而在遍历到左节点后,重点就是如何重新得到根节点,线索二叉树就能够很好的解决这个问题,在遍历完左子树之后,我们一定会处于左子树的最右节点,这时我们就可以通过线索二叉树来重新回到根节点

因此,转换为代码,我们只需要在遍历每个节点时判断是否存在左子树,若存在左子树,则去构建该位置的线索二叉树,并向左遍历, 而若是不存在左子树,则向右遍历

struct TreeNode
{
	int val;
	TreeNode* left = nullptr;
	TreeNode* right = nullptr;
	TreeNode(int v)
		:val(v)
	{}
};
void Morris(TreeNode* root)
{
	while (root)
	{
		if (root->left)
		{
			TreeNode* right_left = root->left;
			while (right_left->right&& right_left != root)
				right_left = right_left->right;
			right_left->right = root;
			root = root->left;
		}
		else
		{
			cout << root->val << "->";
			root = root->right;

		}
	}
	cout << "nullptr";
}
int main()
{
	TreeNode* n1 = new TreeNode(1);
	TreeNode* n3 = new TreeNode(3);
	n3->left = new TreeNode(2);
	n1->right = n3;
	TreeNode* n7 = new TreeNode(7);
	n7->right = new TreeNode(8);
	TreeNode* n6 = new TreeNode(6);
	n6->right = n7;
	n6->left = new TreeNode(5);
	TreeNode* n4 = new TreeNode(4);
	n4->left = n1;
	n4->right = n6;
	Morris(n4);
	return 0;
}

这样看貌似是没有问题的,但是我们要注意,在根节点向后遍历到左子树的最右节点时,下一个遍历的节点会重新根节点,这时,我们已经构建过该节点位置的线索二叉树了,因此这时我们需要之后直接遍历其右节点,因此我们需要对左子树存在的情况做一些处理

void Morris(TreeNode* root)
{
	while (root)
	{
		if (root->left)
		{
			TreeNode* right_left = root->left;
			while (right_left->right&& right_left != root)
				right_left = right_left->right;
			if (right_left != root)
			{
				right_left->right = root;
				root = root->left;
			}
			else
			{
				cout << root->val << "->";
				root = root->right;
			}
		}
		else
		{
			cout << root->val << "->";
			root = root->right;

		}
	}
	cout << "nullptr";
}

这样就不会出现问题了

 同时,为了保证二叉树保持原本的模样,我们可以在第二次遍历到根节点时删除构建的线索二叉树

void Morris(TreeNode* root)
{
	while (root)
	{
		if (root->left)
		{
			TreeNode* right_left = root->left;
			while (right_left->right&& right_left != root)
				right_left = right_left->right;
			if (right_left != root)
			{
				right_left->right = root;
				root = root->left;
			}
			else
			{
				right_left->right = nullptr;
				cout << root->val << "->";
				root = root->right;
			}
		}
		else
		{
			cout << root->val << "->";
			root = root->right;

		}
	}
	cout << "nullptr";
}

 

99. 恢复二叉搜索树 - 力扣(Leetcode)

这道题就是运用的Morris遍历实现的空间复杂度为O(1)的算法

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

finish_speech

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值