栈实现二叉树的先,中,后序遍历

Posted redo19990701

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了栈实现二叉树的先,中,后序遍历相关的知识,希望对你有一定的参考价值。

栈实现二叉树先,中,后序遍历

如果是使用递归来实现二叉树的先,中,后序遍历只需要更改三行代码的位置,但若是使用栈来写那便会有趣得多

根结点与其左右子树间的输出优先级

graph TD 1((根结点))---2((左子树)) 1---3((右子树))
遍历方式 输出优先级
先序 根结点>左子树>右子树
中序 左子树>根结点>右子树
后序 左子树>右子树>根节点

使用栈的规则

栈内元素是指向结点的指针

  1. 只有栈顶元素指向的结点内容才会被输出
    • 方便,不用记忆太多结点
  2. 结点内容输出后指向该结点的指针会立即出栈
    • 避免结点重复输出
  3. 当一个指针出栈时,如果该指针指向的结点的左右子树有未被使用过(未遍历过,或者不为空),那么指向其左右子树的指针将随后入栈,且指向后输出的结点的指针早入栈
    • 入栈是避免信息的丢失,顺序是栈的特性(先进后出)
  4. 栈中初始化只有一个指向根节点的指针
    • 遍历往往以根节点作为基础参数
  5. 当栈为空时遍历结束
    • 即遍历完所有结点(可以由2,3推导出来)

算法

无论是根节点还是左右子树在栈中的表示方式是一样的都是指针,所以我们可以将左右子树各当作是"一个结点",这样问题就简化为具有3个结点的完全二叉树的输出了

在简化的基础上再考虑上特殊情况规则就可以实现了

通用逻辑栈中初始化只有一个指向根节点的指针,当栈为空时遍历结束

先序遍历

推导

graph TD 1((根结点))---2((左子树)) 1---3((右子树))
  • 输出顺序为:根结点->左子树->右子树
  • 结合规则说明
    1. 根结点优先输出,结合规则2可得出栈顶指针指向的结点直接输出,栈顶指针会立即被出栈(提前备份)
    2. 栈顶指针出栈,结合规则3可得出备份指向结点的左子树和右子树的指针会被入栈(非空情况下),且右子树先于左子树入栈
  • 综上所述可得出规律
    1. 先输出栈顶指针指向的结点
    2. 栈顶指针出栈(栈顶指针备份,否则左右子树信息丢失)
    3. 备份指针指向结点的右子树入栈(如果右子树指针不为空)
    4. 备份指针指向结点的左子树入栈(如果左子树指针不为空)
    5. 回到步骤1

C++代码实现

	void preorder_travel() 
		if (root == NULL)
			return;
		std::stack<Node*> s;
		s.push(root);//根指针入栈
		while (!s.empty())
		
			std::cout << s.top()->data << " ";//输出栈顶指针指向的结点
			Node* temp = s.top();//备份栈顶指针
			s.pop();//栈顶指针出栈
			if (temp->R != NULL) s.push(temp->R);//备份指向结点右子树不为空则将其压入栈
			if (temp->L != NULL) s.push(temp->L);//备份指向结点左子树不为空则将其压入栈
		
	

中序遍历

推导

graph TD 1((根结点))---2((左子树)) 1---3((右子树))
  • 输出顺序为:左子树->根结点->右子树
  • 结合规则说明
    1. 左子树优先输出,其次是根节点,但子树不可能直接输出,所以一直搜索栈顶指针指向结点的左子树的左子树的...,直到没有找到没有左子树的结点,然后输出该结点(作为根节点),结合规则2可得出重复压入栈顶指向结点的左子树指针,直到栈顶指向结点的左子树指针为空,然后输出该结点,随后栈顶指针出栈(提前备份)
    2. 由步骤1可得出栈顶指针总是次栈顶指针指向结点的左子树,且左子树(原栈顶指针)输出之后到根结点(现栈顶指针)指向结点输出,结合规则2可得出重复输出栈顶指针指向的结点(输出之前备份,输出之后出栈)
      • 输出栈顶指针,结合规则3可得出若是备份指向结点的右子树存在,则将其入栈
        • 右子树入栈打破2中的前提(栈顶指针总是次栈顶指针指向结点的左子树),所以2停止
    3. 回到到步骤1
  • 综上所述可得出规律
    1. 重复压入栈顶结点左子树指针,直到栈顶结点左子树指针为空,然后输出该结点,栈顶指针出栈(栈顶指针备份,否则左右子树信息丢失)
    2. 重复输出栈顶指针指向的结点(输出之前备份,输出之后出栈),直到备份结点右子树不为空,然后将其右子树入栈,回到1

C++代码实现

void inorder_travel() 
		if (root == NULL)
			return;
		std::stack<Node*> s;
		s.push(root);//根指针入栈
		while (!s.empty())
		
			while (s.top()->L != NULL)//重复将栈顶指针指向结点的左子树压栈,直到栈顶指针指向结点的左子树为空
			
				s.push(s.top()->L);
			
			while (!s.empty())//重复检查
			
				std::cout << s.top->data << " ";//输出栈顶指针指向的结点
				Node* temp = s.top();//备份栈顶指针
				s.pop();//栈顶指针出栈
				if (temp->R != NULL) //如果栈顶指针指向的结点右子树不为空,则将其右子树入栈,退出检查
					s.push(temp->R);
					break;
				
			
		

后序遍历

推导

graph TD 1((栈顶结点))---2((左子树)) 1---3((右子树))
  • 输出顺序为:左子树->右子树->栈顶结点
  • 结合规则说明
    1. 左子树优先输出,其次是右子树,但子树不可能直接输出,所以一直搜索栈顶指针指向结点的左子树的左子树的...,直到没有找到没有左子树的结点,结合规则2可得出重复压入栈顶指向结点的左子树指针,直到栈顶指向结点的左子树指针为空
    2. 由步骤1可得出栈顶指针总是次栈顶指针指向结点的左子树,且左子树(原栈顶指针)输出之后到右子树(现栈顶指针指向结点的右子树)输出
      • 在步骤2前提下反复检查
        • 若是栈顶指针指向结点的右子树遍历过(使用一个last来标记上一次输出的结点指针),输出栈顶指针,栈顶出栈
        • 若是栈顶指针指向结点的右子树为空,输出栈顶指针,栈顶出栈
        • 若是栈顶指针指向结点的右子树不为空,由输出顺序可得出右子树入栈
          • 右子树入栈打破2中的前提(栈顶指针总是次栈顶指针指向结点的左子树),所以2停止
    3. 回到到步骤1
  • 综上所述可得出规律
    1. 重复压入栈顶结点左子树指针,直到栈顶指针指向结点的左子树为空
    2. 重复输判断如果栈顶指针指向结点右子树遍历过或者为空,则栈顶出栈.否则将栈顶指针指向结点右子树入栈
    3. 回到步骤1

C++代码实现

void postorder_travel() 
	if (root == NULL)
		return;
	std::stack<Node*> s;
	s.push(root);//根指针入栈
	while (!s.empty())
	
		while (s.top()->L != NULL)//重复将栈顶指针指向结点的左子树压栈,直到栈顶指针指向结点的左子树为空
		
			s.push(s.top()->L);
		
		Node* last = NULL;//上一次遍历过的指针
		while (!s.empty())//重复检查
		
			if (s.top()->R==NULL||last==s.top()->R) //如果栈顶指针指向结点的右子树为空或者遍历过
				std::cout << s.top()->data << " ";//输出栈顶指向的结点
				last = s.top();//更新指针last
				s.pop();//栈顶指针出栈
			
			else if(s.top()->R!=NULL)//如果栈顶指针指向的结点的右子树不为空
			
				s.push(s.top()->R);//将右子树入栈
				break;//退出检查
			
		
	


整体代码

#include <iostream>
#include <stack>
template <typename T>
class BST 
public:
	BST() :root(NULL) ;
	~BST() ;
	void insert(T data) 
		Node* temp = new Node();
		temp->L = NULL;
		temp->R = NULL;
		temp->data = data;
		if (root == NULL) 
			root = temp;
		
		else 
			Node* tracer = root;
			while (true)
			
				if (tracer->data >= data)
					if (tracer->L == NULL) 
						tracer->L = temp;
						break;
					
					else
						tracer = tracer->L;
				else
					if (tracer->R == NULL) 
						tracer->R = temp;
						break;
					
					else
						tracer = tracer->R;
			
		
	
	void preorder_travel() 
		if (root == NULL)
			return;
		std::stack<Node*> s;
		s.push(root);//根指针入栈
		while (!s.empty())
		
			std::cout << s.top()->data << " ";//输出栈顶指针指向的结点
			Node* temp = s.top();//备份栈顶指针
			s.pop();//栈顶指针出栈
			if (temp->R != NULL) s.push(temp->R);//备份指向结点右子树不为空则将其压入栈
			if (temp->L != NULL) s.push(temp->L);//备份指向结点左子树不为空则将其压入栈
		
	
	void inorder_travel() 
		if (root == NULL)
			return;
		std::stack<Node*> s;
		s.push(root);//根指针入栈
		while (!s.empty())
		
			while (s.top()->L != NULL)//重复将栈顶指针指向结点的左子树压栈,直到栈顶指针指向结点的左子树为空
			
				s.push(s.top()->L);
			
			while (!s.empty())//重复检查
			
				std::cout << s.top->data << " ";//输出栈顶指针指向的结点
				Node* temp = s.top();//备份栈顶指针
				s.pop();//栈顶指针出栈
				if (temp->R != NULL) //如果栈顶指针指向的结点右子树不为空,则将其右子树入栈,退出检查
					s.push(temp->R);
					break;
				
			
		
	
	void postorder_travel() 
		if (root == NULL)
			return;
		std::stack<Node*> s;
		s.push(root);//根指针入栈
		while (!s.empty())
		
			while (s.top()->L != NULL)//重复将栈顶指针指向结点的左子树压栈,直到栈顶指针指向结点的左子树为空
			
				s.push(s.top()->L);
			
			Node* last = NULL;//上一次遍历过的指针
			while (!s.empty())//重复检查
			
				if (s.top()->R==NULL||last==s.top()->R) //如果栈顶指针指向结点的右子树为空或者遍历过
					std::cout << s.top()->data << " ";//输出栈顶指向的结点
					last = s.top();//更新指针last
					s.pop();//栈顶指针出栈
				
				else if(s.top()->R!=NULL)//如果栈顶指针指向的结点的右子树不为空
				
					s.push(s.top()->R);//将右子树入栈
					break;//退出检查
				
			
		
	
private:
	struct Node
	
		Node* L;
		Node* R;
		T data;
	;
	Node* root;
;
int main()
	BST<int> b;
	b.insert(2);
	b.insert(4);
	b.insert(1);
	b.insert(5);
	b.insert(3);
	b.insert(0);
	b.preorder_travel();
	std::cout << std::endl;
	b.inorder_travel();
	std::cout << std::endl;
	b.postorder_travel();

以上是关于栈实现二叉树的先,中,后序遍历的主要内容,如果未能解决你的问题,请参考以下文章

二叉树的后序遍历

二叉树的先序中序后序遍历

一个套路,写出来二叉树的迭代遍历

建立二叉树的二叉链表表示,实现二叉树的先序、中序、后序和按层次遍历,统计并输出结点个数。

给出二叉树的先序和中序遍历,给出后序遍历

创建二叉树非递归完成对二叉树的先序和后序遍历并遍历输出每一层的结点数查找结点P 和结点Q的最近共同祖先