二叉树的递归与非递归遍历

二叉树遍历是二叉树中最基本的问题,其实现的方法非常多,有简单粗暴但容易爆栈的递归算法,还有稍微高级的使用栈模拟递归的非递归算法,另外还有不用栈而且只需要常数空间和线性时间的神奇Morris遍历算法,本文将对这些算法进行讲解和实现。

递归算法

二叉树节点使用以下数据结构进行表示,包括关键字、左儿子、右儿子属性和一个带默认参数的构造函数。
struct成员的默认属性为public,于是可以直接访问。

struct Node
{
	int val;
	Node *left, *right;
	Node(int v = 0, Node *l = NULL, Node *r = NULL) : val(v), left(l), right(r) {}
};

二叉树的递归算法非常简单,设置好递归出口之后,根据遍历的顺序,对当前节点的左右子递归调用自身即可。其前序、中序、后序遍历的代码如下。

void preorder1(Node *root) //递归前序遍历
{
	if (root == NULL) return;
	printf("%d ", root->val);
	preorder1(root->left);
	preorder1(root->right);
}

void inorder1(Node *root) //递归中序遍历
{
	if (root == NULL) return;
	inorder1(root->left);
	printf("%d ", root->val);
	inorder1(root->right);
}

void postorder1(Node *root) //递归后序遍历
{
	if (root == NULL) return;
	postorder1(root->left);
	postorder1(root->right);
	printf("%d ", root->val);
}

栈模拟非递归算法

递归算法的本质是利用函数的调用栈进行,实际上我们可以自行使用栈来进行模拟,这样的算法空间复杂度为O(h),h为二叉树的高度。

前序遍历

首先把根节点入栈,然后在每次循环中执行以下操作:

  • 此时栈顶元素即为当前的根节点,弹出并打印当前的根节点。
  • 把当前根节点的右儿子和左儿子分别入栈(注意是右儿子先入栈左儿子后入栈,这样的话下次出栈的元素才是左儿子,这样才符合前序遍历的顺序要求:根节点->左儿子->右儿子)。

下面是代码实现。

void preorder2(Node *root)//非递归前序遍历
{
	if (root == NULL) return;

	stack<Node *> stk;
	stk.push(root);
	while (!stk.empty())
	{
		Node *p = stk.top(); stk.pop();
		printf("%d ", p->val);
		if (p->right) stk.push(p->right);
		if (p->left) stk.push(p->left);
	}
}

后序遍历

因为后序遍历的顺序是:左子树->右子树->根节点,于是我们在前序遍历的代码中,当访问完当前节点后,先把当前节点的左子树入栈,再把右子树入栈,这样最终得到的顺序为:根节点->右子树->左子树,刚好是后序遍历倒过来的版本,于是把这个结果做一次翻转即为真正的后序遍历。而翻转可以通过使用另外一个栈简单完成,这样的代价是需要两个栈,但就复杂度而言,空间复杂度是O(n)。

void postorder2(Node *root)//非递归后序遍历
{
	if (root == NULL) return;

	stack<Node *> stk, stk2;
	stk.push(root);
	while (!stk.empty())
	{
		Node *p = stk.top(); stk.pop();
		stk2.push(p);
		if (p->left) stk.push(p->left);
		if (p->right) stk.push(p->right);
	}
	while(!stk2.empty())
	{
		printf("%d ", stk2.top()->val);
		stk2.pop();
	}
}

中序遍历

中序遍历稍微复杂,使用一个指针p指向下一个待访问的节点,p初始化为根节点。在每次循环中执行以下操作:

  • 如果p非空,则把p入栈,p变为p的左儿子。
  • 如果p为空,说明已经向左走到尽头了,弹出当前栈顶元素,进行访问,并把p更新为其右儿子。

下面是代码实现。

void inorder2(Node *root)//非递归中序遍历
{
	stack<Node *> stk;
	Node *p = root;
	while (p != NULL || !stk.empty())
	{
		if (p != NULL)
			stk.push(p), p = p->left;
		else
		{
			p = stk.top(); stk.pop();
			printf("%d ", p->val);
			p = p->right;
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值