144. 二叉树的前序迭代遍历推出后序,中序以及层序的实现

本文详细介绍了二叉树的前序、中序和后序遍历算法,包括递归和迭代两种实现方式,并附带代码示例。此外,还简要介绍了层序遍历的方法。

题目:

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例 1:

在这里插入图片描述

输入:root = [1,null,2,3]
输出:[1,2,3]
示例 2:

输入:root = []
输出:[]
示例 3:

输入:root = [1]
输出:[1]
示例 4:
在这里插入图片描述
输入:root = [1,2]
输出:[1,2]
示例 5:
在这里插入图片描述
输入:root = [1,null,2]
输出:[1,2]

提示:

树中节点数目在范围 [0, 100] 内
-100 <= Node.val <= 100

进阶:递归算法很简单,你可以通过迭代算法完成吗?

题解:

方法一:递归
首先我们需要了解什么是二叉树的前序遍历:按照访问根节点——左子树——右子树的方式遍历这棵树,而在访问左子树或者右子树的时候,我们按照同样的方式遍历,直到遍历完整棵树。因此整个遍历过程天然具有递归的性质,我们可以直接用递归函数来模拟这一过程。

定义 preorder(root) 表示当前遍历到 root 节点的答案。按照定义,我们只要首先将 root 节点的值加入答案,然后递归调用 preorder(root.left) 来遍历 root 节点的左子树,最后递归调用 preorder(root.right) 来遍历 root 节点的右子树即可,递归终止的条件为碰到空节点。
代码:

	public int preorder(BiTreeNode p) {
		if (p == null) {
			return 0;
		}
		System.out.print(p.getData() + " ");
		preorder(p.lchild);
		preorder(p.rchild);
		return 1;
	}

方法二:迭代
我们也可以用迭代的方式实现方法一的递归函数,两种方式是等价的,区别在于递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来,其余的实现与细节都相同,具体可以参考下面的代码。
在进行迭代时我们要考虑用栈来对结点的存放和丢弃,声明一个LinkedList,可保存结点数据,并进行头插和尾插。
我们需要一个循环去从根=》左=》右的顺序遍历,循环的条件为root是否为空和栈是否为空,这里判断栈是因为当遍历到第一次root为空时,如果还有未遍历的结点,能够保证继续遍历。
然后如果p!=null,入栈根,list尾插法填入p的数据,p=p.lchild让p变成左孩子,之后一直遍历和入栈左孩子,list尾插法addlast填入左孩子的数据,直到左孩子为空,将栈顶取出(可知栈顶的左孩子为空,设p,q均为栈顶结点), p = p.rchild让p变成右孩子,判断是否有右孩子,如果没有,说明右孩子为空,再次出栈(出栈元素为q的父节点),p变成右孩子,再进行循环判断。整个流程思路就完成了。
下面是我的代码:

public void preorderTraversal(BiTreeNode root) {
        BiTreeNode p = root;
		Stack<BiTreeNode> myStack = new Stack<BiTreeNode>();
		LinkedList<Integer> list = new LinkedList<Integer>();
		while(p!=null || !myStack.isEmpty()) {
			if(p!=null) {
				myStack.push(p);
				list.addLast((int)p.getData());
				p = p.lchild;
			}
			else {
				p = myStack.pop();
				p = p.rchild;
			}
		}
		return list;
    }

后序遍历:

按照这种思路会发现后序遍历reverse后为根=》右=》左,而我们刚说的先序遍历为根=》左=》右,只是把p=p.lchild和p=p.rchild互换顺序而已;但此时list中的数据都是反过来的(由于reverse后为根=》右=》左的原因),我们只需要在插入数据时采用头插法addfirst即可
代码如下:

public LinkedList<Integer> postorder() {
		Stack<BiTreeNode> myStack = new Stack<BiTreeNode>();
		BiTreeNode p = root;
		LinkedList<Integer> list = new LinkedList<Integer>();
		while(p!=null || !myStack.isEmpty()) {
			if(p!=null) {
				myStack.push(p);
				list.addFirst((int)p.getData());
				p = p.rchild;
			}else {
				p=myStack.pop();
				p=p.lchild;
			}
		}
		return list;

	}

顺便把递归方法代码补充一下:

	public int postorder(BiTreeNode p) {
		if (p == null) {
			return 0;
		}
		postorder(p.lchild);
		postorder(p.rchild);
		System.out.print(p.getData() + " ");
		return 1;
	}

中序遍历:

而中序是左=》根=》右,没必要做反转(故使用尾插法),不过不能先处理根元素了,即list不能从根开始添加元素了,我们需要先处理左,(这里需要分清根是先遍历的,而中序遍历应该先处理左,所以不能在遍历的过程中添加到list)左孩子为空之后,取栈顶元素,list添加该元素数据(即根数据),然后让p=p.rchild,使得p变成右孩子,判断右孩子是否存在,存在即入栈,在出栈的时候将数据添加到list中;不存在就继续出栈,判断右孩子是否存在。整个流程就结束了。
代码如下:与先序遍历相比,只改了list添加元素的位置。

public LinkedList<Integer> inorder() {
		BiTreeNode p = root;
		Stack<BiTreeNode> myStack = new Stack<BiTreeNode>();
		LinkedList<Integer> list = new LinkedList<Integer>();
		while(p!=null || !myStack.isEmpty()) {
			if(p!=null) {
			myStack.push(p);
			p=p.lchild;	
			}else {
				p = myStack.pop();
				list.addLast((int)p.getData());
				p = p.rchild;
			}
		}
		return list;
	}

递归代码如下:

public int inorder(BiTreeNode p) {
		if (p == null) {
			return 0;
		}
		inorder(p.lchild);
		System.out.print(p.getData() + " ");
		inorder(p.rchild);
		return 1;
	}

总结:

先序和后序遍历由于都可以看做遍历的元素和要处理的元素都为根,所以在遍历的过程中List添加元素;中序遍历是遍历到左孩子为空后再处理根,即先遍历到左孩子为空后处理,所以从左开始遍历到左孩子为空,再将栈顶数据进行添加到List中。
如果感觉上面的迭代写法不好记忆的话,请看我的这篇文章https://blog.csdn.net/a12355556/article/details/117043765
顺便写一下层序遍历吧

主要是使用队列进行操作。

 //BFS--迭代方式--借助队列
 	public List<List<Integer>> resList = new ArrayList<List<Integer>>();
 	
    public void checkFun02(TreeNode node) {
        if (node == null) return;
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(node);

        while (!que.isEmpty()) {
            List<Integer> itemList = new ArrayList<Integer>();
            int len = que.size();

            while (len > 0) {
                TreeNode tmpNode = que.poll();
                itemList.add(tmpNode.val);

                if (tmpNode.left != null) que.offer(tmpNode.left);
                if (tmpNode.right != null) que.offer(tmpNode.right);
                len--;
            }

            resList.add(itemList);
        }

    }

结束

在编程中,二叉树遍历通常是递归或迭代的方式来进行的。这里我将简述四种常见的遍历方法: 1. **前序遍历 (Preorder Traversal)**: 先访问根节点,然后遍历左子树,最后遍历右子树。递归实现如下: ```python def preorder_traversal(root): if root is not None: print(root.val) # 访问根节点 preorder_traversal(root.left) # 遍历左子树 preorder_traversal(root.right) # 遍历右子树 ``` 2. **中遍历 (Inorder Traversal)**: 先遍历左子树,然后访问根节点,最后遍历右子树。递归版本: ```python def inorder_traversal(root): if root is not None: inorder_traversal(root.left) # 遍历左子树 print(root.val) # 访问根节点 inorder_traversal(root.right) # 遍历右子树 ``` 3. **后序遍历 (Postorder Traversal)**: 先遍历左子树和右子树,最后访问根节点。递归实现: ```python def postorder_traversal(root): if root is not None: postorder_traversal(root.left) postorder_traversal(root.right) print(root.val) # 访问根节点 ``` 4. **层次遍历 (Level Order Traversal or Breadth-first Search, BFS)**: 按照从上到下,从左到右的顺逐层遍历。通常借助队列数据结构实现: ```python from collections import deque def level_order_traversal(root): if root is None: return queue = deque([root]) while queue: node = queue.popleft() print(node.val, end=" ") # 输出当前层所有节点 if node.left: queue.append(node.left) if node.right: queue.append(node.right) ```
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值