二叉树的三种遍历方式:递归、栈、循环

开发者福利!热门AI工具限时免费用 购周边即赠Coding Plan Lite,Claude Code、Cursor等20+工具畅享,效率翻倍! 阅读详情

          三种方法中,递归最为简单,栈次之,循环最为麻烦。递归的深度如果太大则会导致栈溢出;栈的方式需要额外的辅助空间;循环编程最麻烦。

          首先是递归:

//递归方法
void midPrint_r(TreeNode* root)
{//中序遍历
	if(root==NULL)
		return;
	if(root->left)
		midPrint_r(root->left);
	cout<<root->val<<"   ";
	if(root->right)
		midPrint_r(root->right);
}

void prePrint_r(TreeNode* root)
{//前序遍历
	if(root==NULL)
		return;
	cout<<root->val<<"   ";
	if(root->left)
		prePrint_r(root->left);	
	if(root->right)
		prePrint_r(root->right);
}

void postPrint_r(TreeNode* root)
{//中序遍历
	if(root==NULL)
		return;
	if(root->left)
		postPrint_r(root->left);	
	if(root->right)
		postPrint_r(root->right);
	cout<<root->val<<"   ";
}

    栈方法:先循环把结点压入到栈中,然后再逐个弹出;

//循环堆栈方法
void midPrint_l(TreeNode* root)
{
	stack<TreeNode*> s;
	TreeNode *cur = root;
	while(!s.empty() || cur != NULL)
	{
		while(cur != NULL)
		{
			s.push(cur);
			cur = cur->left;
		}
		cur = s.top();
		s.pop();
		cout<<cur->val<<" ";
		cur = cur->right;
	}
}

void prePrint_l(TreeNode* root)
{
	stack<TreeNode*> s;
	TreeNode *cur = root;
	while(!s.empty() || cur != NULL)
	{
		while(cur != NULL)
		{
			cout<<cur->var<<" ";
			s.push(cur);
			cur = cur->left;
		}
		cur = s.top();
		s.pop();
		cur = cur->right;
	}
}

void postPrint_l(TreeNode* root)
{
	//the original method
	/*
	We use a prev variable to keep track of the previously-traversed node.
	Let’s assume curr is the current node that’s on top of the stack. When
	prev is curr‘s parent, we are traversing down the tree. In this case,
	we try to traverse to curr‘s left child if available (ie, push left
	child to the stack). If it is not available, we look at curr‘s right
	child. If both left and right child do not exist (ie, curr is a leaf node),
	we print curr‘s value and pop it off the stack.If prev is curr‘s left
	child, we are traversing up the tree from the left. We look at curr‘s
	right child. If it is available, then traverse down the right child (ie,
	push right child to the stack), otherwise print curr‘s value and pop it
	off the stack.If prev is curr‘s right child, we are traversing up the tree
	from the right. In this case, we print curr‘s value and pop it off the stack.
	*/
	if(root == NULL)
	{
		return;
	}
	stack<TreeNode*> s;
	s.push(root);
	TreeNode *cur = NULL;
	TreeNode *pre = NULL;
	while(!s.empty())
	{
		cur = s.top();
		//left down or right down
		if( pre == NULL || pre->left == cur || pre->right == cur)
		{//有左子树压左子树,有右子树压右子树,
		//都没有说明是叶子结点,直接打印并弹出
			if(cur->left != NULL)
			{//先压左子树,若左子树为空则压右子树
				s.push(cur->left);
			}
			else if(cur->right != NULL)
			{
				s.push(cur->right);
			}
			else
			{//左右子树均为空
				cout<<cur->var<<" ";
				s.pop();
			}
		}
		//left up
		else if(cur->left == pre)
		{//左边开始返回
			if(cur->right != NULL)
			{//若右孩子不为空则压入,否则,打印
				s.push(cur->right);
			}
			else
			{
				cout<<cur->var<<" ";
				s.pop();
			}
		}
		//right up
		else if(cur->right == pre)
		{//从右边返回,说明右侧已经打印完,则可以直接打印
			cout<<cur->var<<" ";
			s.pop();
		}
		pre = cur;
	}
	// the easy method
	/*stack<TreeNode*> s;
	s.push(root);
	TreeNode *cur = NULL;
	TreeNode *pre = NULL;
	while(!s.empty())
	{
	    cur = s.top();
	    if(pre == NULL || pre->left == cur || pre->right == cur)
		{
	        if(cur->left != NULL)
			{
	            s.push(cur->left);
	        }
	        else if(cur->right != NULL)
			{
	            s.push(cur->right);
	        }
	    }
	    else if(cur->left == pre)
		{
	        if(cur->right != NULL)
			{
	            s.push(cur->right);
	        }
	    }
	    else
		{
	        cout<<cur->var<<" ";
	        s.pop();
	    }
	    pre = cur;
	}*/
}

循环:利用叶子结点左右指针为空的特点,给叶子结点设置其直接后继,输出完该子结点后,再返回其直接后继;

//不用辅助空间的方式
void midPrint_m(TreeNode *root)
{
	TreeNode *cur = root;
	TreeNode *pre = NULL;
	while(cur != NULL)
	{
		if(cur->left == NULL)
		{//左孩子为空,则直接输出
			cout<<cur->var<<" ";
			cur = cur->right;
		}
		else
		{
			pre = cur->left;
			while( pre->right != NULL && pre->right != cur )
			{//找到cur的直接前驱
				pre = pre->right;
			}
			if(pre->right == NULL)
			{//设置后继结点
				pre->right = cur;
				cur = cur->left;
			}
			else
			{
				pre->right = NULL;//重新设置为空
				cout<<cur->var<<" ";
				cur = cur->right;
			}
		}
	}
}

void prePrint_m(TreeNode *root)
{//基本同于中遍历
	TreeNode *cur = root;
	TreeNode *pre = NULL;
	while(cur != NULL)
	{
		if(cur->left == NULL)
		{
			cout<<cur->var<<" ";
			cur = cur->right;
		}
		else
		{
			pre = cur->left;
			while( pre->right != NULL && pre->right != cur )
			{
				pre = pre->right;
			}
			if(pre->right == NULL)
			{
				pre->right = cur;
				cout<<cur->var<<" ";
				cur = cur->left;
			}
			else
			{
				pre->right = NULL;
				cur = cur->right;
			}
		}
	}
}
//this is the most difficult algorithm
void reverse_out(TreeNode *from,TreeNode *to)
{
	//first reverse from->to
	//reverse
	TreeNode *cur = from;
	TreeNode *post = cur->right;
	while(cur != to)
	{
		TreeNode *tmp = post->right;
		post->right = cur;
		cur = post;
		post = tmp;
	}
	//already reverse,output list
	TreeNode *traversal = cur;
	while( cur != from )
	{
		cout<<cur->var<<" ";
		cur = cur->right;
	}
	cout<<cur->var<<" ";
	//reverse original
	cur = to;
	post = cur->right;
	while(cur != from)
	{
		TreeNode *tmp = post->right;
		post->right = cur;
		cur = post;
		post = tmp;
	}
	//restore to's right
	to->right = NULL;
}
void postPrint_m(TreeNode *root)
{
	TreeNode *newroot = new TreeNode(0);
	newroot->left = root;
	newroot->right = NULL;
	TreeNode *cur = newroot;
	TreeNode *pre = NULL;
	while(cur != NULL)
	{
		if(cur->left == NULL)
		{
			cur = cur->right;
		}
		else
		{
			pre = cur->left;
			while(pre->right != NULL && pre->right != cur)
			{
				pre = pre->right;
			}
			if(pre->right == NULL)
			{
				pre->right = cur;
				cur = cur->left;
			}
			else
			{
				pre->right = NULL;
				reverse_out(cur->left,pre);
				cur = cur->right;
			}
		}
	}
}

    可见,前序、中序编程都比较简单,一量涉及后序就会比较麻烦。

二叉树篇1-遍历-递归遍历遍历的使用 本篇的重点是比较并理解递归与迭代的写法。 先验: 关于二叉树的概念,种类在STL容器篇中说明过,因此这里直接用 阅读详情

相关推荐

C++实现递归,非递归遍历二叉树(前序,中序,后序)

初学二叉树那会儿,始终掌握不好二叉树遍历方法,更认为非递归遍历晦涩难懂没有掌握的意义。实际上非递归遍历方法很有用处,由于每次递归都需要将函数的信息入,当递归层数太深很容易就导致溢出,所以这个时候就必须用到非递归遍历二叉树了。而且,当看懂非递归遍历后,你会发现,其实非递归也很简单。 我们知道,要想处理二叉树,离不开其遍历方式遍历二叉树三种方式,前序遍历,中序遍历,后序遍历,每种...

Hairy_Monsters的博客 4160

二叉树三种遍历方式递归循环 分类: C/C++ ...

三种方法中,递归最为简单,次之,循环最为麻烦。递归的深度如果太大则会导致溢出;方式需要额外的辅助空间;循环编程最麻烦。 首先是递归: //递归方法 void midPrint_r(TreeNode* root) {//中序遍历 if(root==NULL) return; if(root->left) midPrin...

weixin_30445169的博客 214

二叉树递归与非递归遍历

二叉树遍历 常见的二叉树遍历三种方式:先序遍历(根结点,左子,右子) 中序遍历(左子,根结点,右子)    后续遍历(左子,右子,根结点) 这三种方式有分别可以用递归和非递归来实现 先序遍历的主要步骤:(参考下图) 1.从根结点开始先压左路结点,并访问结点,直到把根结点和左路结点全部压入。 2.若左子和为空,说明左路和根结点已经全部压并且已经访问过了,开始取顶元素来访问上一层父节点的右子。把 右子看成子问题继续进行1步骤。 3.依次进行上述1和2

TerryZjl的博客 1651

数据结构-(C语言代码)

(stack)又名堆,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为顶,相对地,把另一端称为底。向一个插入新元素又称作进、入或压,它是把新元素放到顶元素的上面,使之成为新的顶元素;从一个删除元素又称作出或退,它是把顶元素删除掉,使其相邻的元素成为新的顶元素。 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct.

weixin_50246370的博客 5985

数据结构——二叉树前序、中序、后序及层次四种遍历(java语言版)

数据结构——二叉树前序、中序、后序及层次四种遍历(java语言版)

qq_51917627的博客 1406

数据结构~15.使用自定义的来优化二叉树遍历

使用自定义的来优化二叉树遍历 本文是上一篇文章的后续,详情点击该链接~ 前言        在前面遍历二叉树的操作里,基本上都是使用递归实现的,因为递归解决问题的方式会相对循环来说要更加简单一些。但是我们要知道,对于计算机而言,却未必如此。 递归的缺点        递归的优点,想必在之前也已经体会到了。使用递归解决问题,往往思路清晰,简单易懂。但是我们要知道,递归

qq_41424688的博客 3万+

【C++】【LeetCode】【二叉树的前序、中序、后序遍历】【递归+非递归

默认情况下,一个线程的空间大小为8MB,当递归的深度太深,我们的程序就容易崩溃如果递归的深度太深,空间不大,那么程序容易崩溃改成非递归1.改成循环(比方说斐波那契)2.通过改成循环为什么递归会空间溢出,非递归不会空间溢出?1.如果是循环的话,形成迭代,消耗的空间很可能是0(1)2.如果使用的是通过来改成循环的话,我们的开辟的空间在内存的堆区(内存中的空间不大,但是堆的空间很大,几乎不存在空间溢出的问题)所以这里我们将递归遍历改成非递归

芊樱烛渊的博客 856

如何在不使用递归的情况下遍历一颗二叉树

面试的时候,往往面试官会强制你写出非递归的版本,网上关于非递归版本的介绍也有很多,这里我分享一个自己认为是比较好记的版本。 显然,我们需要用一个stack来模拟递归时的函数调用。对于三种遍历,我们都使用push当前节点->push左子->pop左子->push右子->pop右子方式。但是cout时机会有所不同。 对于前序遍历来说,每次访问到一个节点就cout; 对于中序遍历来说,每次将右子节点进时,把当前节点cout; 对于后序遍历来说,每次pop的时候cout。 使用工

weixin_43907316的博客 422

重生之我在代码随想录刷算法第十一天 | 二叉树递归遍历二叉树的迭代遍历二叉树的层序遍历

60天算法训练营第11天,今天登场的是二叉树的各种遍历!

qq_73210658的博客 463

二叉树三种遍历方式总结(Java)

以下主要是记录三种遍历方式的特征以及以翻转二叉树为例子,写代码。

m0_72833525的博客 229

二叉树的前序、中序、后序遍历

递归方式: /**   * 实现二叉树的创建、前序遍历、中序遍历和后序遍历   **/   package DataStructure;      /**   * Copyright 2014 by Ruiqin Sun   * All right reserved   * created  on 2014-9-9 下午2:34:15   **/   public

qq991029781的专栏 488

二叉树遍历递归

原文http://www.wangchao.net.cn/bbsdetail_66513.html   1.先序遍历递归算法   #define maxsize 100   typedef struct   {    Bitree Elem[maxsize];    int top;   }SqStack;   void PreOrderUnrec(Bitree t)   {

一碗米线 794

迭代法遍历二叉树(前中后序列)

根结点入非空循环每次循环,若弹出元素非空则按照前中后序的顺序反向压,其中中根结点为了适配中序遍历,不能立马丢弃,按照顺序压,并且在其后压入空节点标记若结点为空,则弹两次,得到结点,插入结果切片中。

weixin_73809064的博客 239

94-二叉树的中序遍历

题目 给定一个二叉树的根节点 root ,返回它的 中序 遍历。 示例 1: 输入:root = [1,null,2,3] 输出:[1,3,2] 示例 2: 输入:root = [] 输出:[] 示例 3: 输入:root = [1] 输出:[1] 示例 4: 输入:root = [1,2] 输出:[2,1] 示例 5: 输入:root = [1,null,2] 输出:[1,2] 题解一 递归 class Solution { List<Integer> res; pu

codrab的博客 257

二叉树三种遍历递归实现

二叉树遍历实现

ryjflyshy的博客 4万+

方式实现二叉树遍历

递归方式就是函数自身调用自身,当递归每次调用自身时,可以看作是入的过程,当递归条件满足后,结束时递归再一级一级的返回,返回过程可以看作是出的过程。递归的实现过程可以看出都是符合“先进后出,后入先出”的原则,所以递归方式其实可以转化为方式来实现。对于二叉树遍历,先序、中序、后序遍历都可以用到递归方法实现,既然递归可以转化为,那么如何把对二叉树遍历也改为用的思想来实现?对于下面这个...

大力海棠的博客 1万+

三种递归遍历二叉树的方法

   就以这个为例,来讲讲二叉树的非递归遍历。 先序遍历: 先序遍历结果为3 4 6 5 8 9,就拿的左枝为例,3是根,打印,4是3的左孩子,打印,6是4的左孩子,打印,6的左孩子为空,所以返回到4,然后去找4的右孩子,4的右孩子也为空,返回到3,这就是左子遍历的过程。然后非递归主要用到来存储结点,先进后出,所以应该是右孩子先入,左孩子后入,这样pop就能先得到左孩子。先将根结点3入,接下来就是开始循环循环结束的条件就是为空,先弹出顶,再打印顶,如果.

weixin_51656756的博客 8781

二叉树遍历详解(递归遍历、非递归遍历,Morris遍历

一、前言 《二叉查找全面详细介绍》中讲解了二叉树操作:搜索(查找)、遍历、插入、删除。其中遍历深度优先遍历(DFS)按照实现方法可以分为:递归遍历实现、非递归遍历实现、Morris遍历实现,文中只给了代码,没有对实现过程进行讲解,本文将对递归遍历实现、非递归遍历实现、Morris遍历实现这三类实现方法进行讲解。 二、三类实现方法特点 递归实现 编码简单,易于理解; 隐式使用存储当前没有处理完的节点信息; 若深度大,递归深度大可能会超过堆保留大小; 时间复杂度:O(n),空间复杂度.

行走 4165

二叉树的非递归遍历

二叉树的非递归遍历 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的。对于二叉树,有前序、中序以及后序三种遍历方法。因为的定义本身就是递归定义,因此采用递归的方法去实现三种遍历不仅容易理解而且代码很简洁。而对于遍历若采用非递归的方法,就要采用去模拟实现。在三种遍历中,前序和中序遍历的非递归算法都很容易实现,非递归后序遍历实现起来相对来说要难一点。 ​ 三种遍历方式访问节点的顺序是一致的,不同之处在于,有的遍历流程把访问到的节点暂存起来,达成某种条件后再将节点输出。

qq_43169752的博客 1万+
上一篇: leetCode(13):Remove Duplicates from Sorted List II
下一篇: leetCode(14):Invert Binary Tree and Same Tree
朱传林
博客等级 码龄14年 31粉丝 143原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值