94. 二叉树的中序遍历
144. 二叉树的前序遍历
145. 二叉树的后序遍历
给定一个二叉树,返回它的前序、中序、后序 遍历。
示例:
输入: [1,null,2,3]
前序输出: [1,2,3]
中序输出: [1,3,2]
后序输出: [3,2,1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
来源:力扣(LeetCode)
链接:二叉树的中序遍历 - 力扣(LeetCode)
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目分析:
树的遍历方式主要有2种,深度优先遍历和广度优先遍历,广度遍历主要为层级遍历,而深度遍历主要分前序遍历、中序遍历、后序遍历,这3种遍历说的前序、中序和后序是指父节点,顺序如下,切莫理解为前序先遍历左节点,后序先遍历右节点。
前序遍历,父节点先遍历,遍历顺序为:父节点-》左节点-》右节点
中序遍历,父节点中间遍历,遍历顺序为:左节点-》父节点-》右节点
后序遍历,父节点后遍历,遍历顺序为:左节点-》右节点-》父节点
具体的解法相信只要学习过数据结构,都会的递归遍历,代码非常简单。
解法一:递归遍历
由于给的案例模板里面的方法带出参,所以我们需要自己写一个递归函数,把list作为入参传到递归函数中。
public List<Integer> preorderTraversal(TreeNode root);
public List<Integer> inorderTraversal(TreeNode root);
public List<Integer> postorderTraversal(TreeNode root);
定义树节点:
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
递归代码如下:
1、前序遍历
/**
* 二叉树的前序遍历
* @param root
* @return
*/
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
preorderTraversal(root,res);
return res;
}
/**
* 前序遍历递归函数
* @param root
* @param res
*/
private void preorderTraversal(TreeNode root, List<Integer> res) {
if(root != null){
res.add(root.val);
preorderTraversal(root.left, res);
preorderTraversal(root.right,res);
}
}
2、中序遍历
/**
* 中序遍历,先遍历左节点,后中节点,后右节点
*
* @param root
* @return
*/
public static List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
inorderTraversal(root, list);
return list;
}
/**
* 递归函数
*
* @param root
* @param list
*/
private static void inorderTraversal(TreeNode root, List<Integer> list) {
if (root != null) {
inorderTraversal(root.left, list);
list.add(root.val);
inorderTraversal(root.right, list);
}
}
3、后序遍历
/**
* 二叉树的后序遍历
* @param root
* @return
*/
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
// 递归遍历
postorderTraversal(root,res);
return res;
}
/**
* 后序遍历递归函数
* @param root
* @param res
*/
private void postorderTraversal(TreeNode root, List<Integer> res) {
if(root!=null){
// 先遍历左节点
postorderTraversal(root.left,res);
// 遍历右节点
postorderTraversal(root.right,res);
// 遍历父节点
res.add(root.val);
}
}
复杂度分析:
时间复杂度:O(n)
空间复杂度:O(n)
解法二:栈遍历
学过jvm的同学都知道,没学过也没关系,记住结论就行,java中所有方法调用都保存在栈中,而递归就是不停的调用方法,所以递归方法调用多少次,每次入参是什么等都保存在栈中,只不过是系统帮我们实现了。当然,我们自己也可以用栈去遍历树而不适用递归方法。所有的递归方法都可以用栈实现哦。
1、前序遍历
/**
* 二叉树的前序遍历-栈
* @param root
* @return
*/
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
// 申请一个栈空间,保存节点
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
while(curr != null || !stack.isEmpty()){
// 当前节点的左节点深度遍历
while(curr!=null){
res.add(curr.val);
stack.push(curr);
curr = curr.left;
}
// 当前节点的左节点访问完毕,访问右节点,对右节点的左节点继续深度遍历
curr = stack.pop();
curr = curr.right;
}
return res;
}
2、中序遍历
public List<Integer> inorderTraversal(TreeNode root) {
// 申请一个栈空间,保存节点
Stack<TreeNode> stack = new Stack<>();
List<Integer> res = new ArrayList<>();
// 定义一个指针,指向根节点
TreeNode curr = root;
while (curr != null || !stack.isEmpty()) {
// 每指向一个节点,把该节点push到栈中,且将左节点push到栈中
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
// 左节点不存在,因为是中序遍历,栈中最后一个元素出栈并访问
curr = stack.pop();
res.add(curr.val);
// 指针指向右节点,开始下一个循环,访问该右节点的左节点
curr = curr.right;
}
return res;
}
3、后序遍历,在leetcode中,后序遍历被定义为困难级别,当然,递归算法不可能是困难级别,定义困难的主要原因是利用栈的迭代算法。
从前面的算法中我们可以看出:前序的算法是 父-》左-》右,后序的算法顺序是 左-》右-》父,而在迭代算法中最后访问父节点实现起来比较困难,此时我们可以转换一下思路,我们将后序算法的顺序反向计算,即 左《-右《-父,先遍历父节点,再遍历右节点,再遍历左节点,将遍历结果反向输出,或者保存的时候在链表的最前端保存,同样可以达到后序遍历的效果,总结就是:用前序遍历的算法实现后序遍历。
具体的代码在前序遍历的技术上略作修改即可,具体代码如下:
/**
* 二叉树的后序遍历-栈
* @param root
* @return
*/
public List<Integer> postorderTraversal3(TreeNode root) {
LinkedList<Integer> res = new LinkedList<>();
// 定义一个栈
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
while(curr != null || !stack.isEmpty()){
// 当前节点的右节点深度遍历
while(curr!=null){
res.addFirst(curr.val);
stack.push(curr);
curr = curr.right;
}
// 当前节点的左节点访问完毕,访问右节点,对右节点的左节点继续深度遍历
curr = stack.pop();
curr = curr.left;
}
return res;
}
复杂度分析:
时间复杂度:O(n)
空间复杂度:O(n)