关于二叉树的所有遍历方式

递归遍历

首先我们要说明一下递归的三要素

  1. 确认返回值和传入值
  2. 确认终止条件
  3. 确认单层递归的逻辑

以二叉树来学习递归几乎是最好的学习递归方式;
接下来我们来仔细阐述一下在二叉树中的这三个要素
首先,要做到递归,第一步就是定义一个可以循环利用的函数,那么定义函数第一步除去起函数名就是确认传入条件,又因为是可循环的函数那么确认return返回条件也是必须的。
那么一力扣二叉树为例,递归要传入谁呢,毫无疑问就是根节点root。那么可以得到以下函数。

function dfs(root){}

那么接下来是否要确定返回条件了呢,需要注意的是,我们所说的返回条件不一定是return,而应该是收获胜利的果实。同样以力扣题为例他需要我们返回一个数组,那么明显我们可以返回一个全新的数组res

const res = []

这里需要注意的是关于返回结果,返回结果前必须要验证元素非空也就是不能==null

if(root===null)return ;

这里碰巧的是验证非空就是关键第二步:确认终止条件,一旦该节点为空说明他的父节点没有左节点或者右节点(是左还是右就取决于这个节点是左还是右嘛)
接下来我们来到了最后一步:确认单层递归的逻辑
我们可以想一想,当我们把根节点放入函数时,它验证不为空,然后其val放入res数组当做结果(以前序遍历为例)。接下来它的左右节点也是完整的节点,也可以当做root节点来处理,所以此时只需要把他们两放入函数中就行。

dfs(root.left)
dfs(root.right)

完整代码(以前序遍历为例)

var preorderTraversal = function(root) {
	const res = []//1定义返回结果
	function dfs(root){//1确认传入参数
		if(root==null) return //2确定终止条件
		res.push(root.val)//1确认返回结果
		dfs(root.left)//3确认单层递归的逻辑
		dfs(root.right)//3确认单层递归的逻辑
	}
	dfs(root)
	return res
}

中序遍历:

var inorderTraversal = function(root) {
    let res=[];
    const dfs=function(root){
        if(root===null){
            return ;
        }
        dfs(root.left);
        res.push(root.val);
        dfs(root.right);
    }
    dfs(root);
    return res;
};

后序遍历:

var postorderTraversal = function(root) {
    let res=[];
    const dfs=function(root){
        if(root===null){
            return ;
        }
        dfs(root.left);
        dfs(root.right);
        res.push(root.val);
    }
    dfs(root);
    return res;
};

迭代遍历

首先说明一下,大家看递归遍历可能会觉得前中后序遍历只是放入根节点的val的顺序区别;其实并不是这样的。关于前序遍历它是先放入根节点val的值没错,但中序和后序遍历并不是如此,这两个与前序遍历不同的是,他们是需要先像遍历链表一样来遍历最远端的左节点的。然后在进行收获胜利果实。而这在递归遍历代码中是很难体现出来的。而两种截然不一样的方式也就造就不同迭代代码结构。

迭代前序遍历

  1. 定义一个栈,用来模拟递归函数的先进后出
  2. 不断进行入栈以及出栈,直到栈为空

也许大家对第一点比较能够理解,但是第二点什么叫不断进行出入栈呢。
其实就是分别把根节点右节点左节点依次入栈,为什么是先右后左这就跟栈的特性相关,尤为入栈和出栈顺序相反嘛。继续说回来,其实完全可以把根右左作为循环一直循环下去,直到栈空。
完整代码:

var preorderTraversal = function(root) {
	const res = []
	if(!root) return 
	let stack = [root]
	let cur = null
	while(stack.length){
		cur = stack.pop()
		res.push(cur.val)
		cur.left && stack.push(cur.left)
		cur.right && stack.push(cur.right)
	}
	return res
}

中序遍历

  1. 定义一个指针,遍历到最后一个
  2. 再在栈里弹出
    完整代码:
var inorderTraversal = function(root) {
	if(!root) return 
	let stack = []
	let res = []
	let cur = root
	while(cur || stack.length){
		if(cur){
			stack.push(cur)
			cur=cur.left
		}else{
			cur = stac.pop()
			res.push(cur.val)
			cur=cur.right
		}
	}
	return res
};

后序遍历可以使用一个巧方法
改变前序遍历的顺序在倒序输出

层序遍历

关于层序遍历,因为是从左到右,可以使用队列来解决,那么如何符分层级是最终问题。
首先,可以定义一个变量来记录正在遍历层的节点数量,如何记录正确呢,可是队列将要遍历时保证队列中只有这一层。
完整代码:

var levelOrder = function(root) {
   let res =[]//所有层结果
   let queue=[root]
   if(!root) return res
   while(queue.length){
   	let len = queue.length//记录这次遍历层数
   	let arr =[]//一层的结果数组
   	while(len--){//开始遍历该层
			let node = queue.shift()//不断根据len长度弹出
			arr.push(node.val)
			node.left && queue.push(node.left)
			node.right && queue.push(node.right)
		}
		res.push(arr)
   }
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值