二叉树的Morris遍历

二叉树的普通遍历算法有很多,但它们都无法做到额外空间复杂度O(1),因为它们总要用到栈来保存信息,才能够从下层回到上层。这里介绍Morris遍历,能够做到时间复杂度为O(n),额外空间复杂度为O(1)。

Morris中序遍历

Morris遍历其实就是利用了树中大量的null指针。如下图,我们现在先来考虑中序遍历,对于节点4,什么时候会打印它呢?

由中序遍历的序列得知,在节点4的左子树都打印完毕后,才会轮到节点4,接着就是节点4的右子树了。因为节点4的左子树最后一个打印的节点是左子树的最右节点3,那么我们先把这个节点的right指针指向节点4,当打印到该点时,不就可以顺着右指针回到节点4了。

所以Morris中序遍历的流程大概如此:
对于一个节点cur,找到它左子树的最右节点,看这个节点的right指针是null还是指向cur。
如果是null,说明左子树还没处理过,更新该指针为cur,然后进入左子树继续上述流程。
如果该指针已经是cur了,说明左子树已经处理完毕,现在是处理完毕后顺这最右节点的right指针回到该cur节点的,需先将该right指针恢复为null,处理该cur节点后进入右子树重复流程⑴。
在这里插入图片描述

代码:

void MorrisInOrder(Node* head)
{
	if (head == nullptr)
		return;
	// 当前遍历到节点
	Node* cur = head;
	// 左子树的最右节点
	Node* next;
	while (cur)
	{
		// 处理左子树的最右节点
		next = cur->left;
		if (next)
		{
			while (next->right && next->right != cur)
				next = next->right;
			// 不等于cur,说明未遍历过,设置线索,并跳到该左子树
			if (next->right != cur)
			{
				next->right = cur;
				cur = cur->left;
				continue;
			}
			// 否则说明该左子树已处理完毕,恢复原指针
			else
			{
				next->right = nullptr;
			}
		}
		// 该节点的左子树已处理完毕,转向右子树
		cout << cur->value << " ";
		cur = cur->right;
	}
}

先序遍历

先序遍历只是在中序遍历的基础上调整了一下打印时机,在第一次遍历到节点时就打印该节点。

void MorrisPreOrder(Node* head)
{
	if (head == nullptr)
		return;
	Node* cur = head;
	Node* next;
	while (cur)
	{
		next = cur->left;
		if (next)
		{
			while (next->right && next->right != cur)
				next = next->right;
			if (next->right != cur)
			{
				cout << cur->value << " ";
				next->right = cur;
				cur = cur->left;
				continue;
			}
			else
			{
				next->right = nullptr;
			}
		}
		else
		{
			cout << cur->value << " ";
		}
		cur = cur->right;
	}
}

后序遍历

同样,后序遍历也是中序遍历的变种,但其调整更加复杂。

简单来说就是,对于一个节点,先处理左子树,但并不是把整棵左子树都处理完,而是留下该左子树的右边界,当回到该节点时,才逆序打印整个右边界,这样左子树就处理完了。

这时回到该节点但并不处理该点,而是直接进入右子树处理,待到右子树顺着最右节点回到上层父节点时,该节点才做为上层父节点的左子树右边界被打印。

如下图,对于节点4,先处理左子树的非右边界节点1,待到回到节点4才处理左子树的右边界节点3、2。
在这里插入图片描述

右边界的处理方法:先把边界的右指针逆序,依次打印后,重新逆序恢复。

在这里插入图片描述

代码:

Node* ReverseEdge(Node* head)
{
	Node* cur = head;
	Node* pre = nullptr;
	Node* next;
	while (cur)
	{
		next = cur->right;
		cur->right = pre;
		pre = cur;
		cur = next;
	}
	return pre;
}

void PrintEdge(Node* head)
{
	Node* tail = ReverseEdge(head);
	Node* cur = tail;
	while (cur)
	{
		cout << cur->value << " ";
		cur = cur->right;
	}
	ReverseEdge(tail);
}

void MorrisPosOrder(Node* head)
{
	if (head == nullptr)
		return;
	Node* cur = head;
	Node* next;
	while (cur)
	{
		next = cur->left;
		if (next)
		{
			while (next->right && next->right != cur)
				next = next->right;
			if (next->right != cur)
			{
				next->right = cur;
				cur = cur->left;
				continue;
			}
			else
			{
				next->right = nullptr;
				PrintEdge(cur->left);
			}
		}
		cur = cur->right;
	}
	PrintEdge(head);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值