首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >二叉树之遍历OJ(含迭代)

二叉树之遍历OJ(含迭代)

作者头像
趙卋傑
发布2026-01-12 15:07:22
发布2026-01-12 15:07:22
1270
举报

1.递归实现

前言

此篇文章我们将详细理解二叉树的遍历,想必大家跟我一样第一次学习二叉树非常头疼,它是怎么递归的呀?怎么搞也搞不明白,想将递归的过程画出来却越画越乱,不画却又不理解,因此借此篇文章谈谈我是怎么理解二叉树中的递归的

在写递归时我们可以遵循以下四个步骤:

  1. 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
  2. 将原问题化为子问题:类比循环我们每次执行的代码应该是相同的,但子问题是需要把计算结果返回上一级问题,也就是递归的每一层都要有返回值来给到上一层
  3. 确定非边界条件:确定非边界条件时我们不要一上来就陷入递归中的细节,我们可以将左子树看作一个整体,右子树看作一个整体来思考确定非边界条件
  4. 确定边界条件:我们通过子问题来确定递归的边界条件,由于子问题规模较小,当代码一层一层往里递的时候总会有一个终止的条件,即边界条件,此时就直接返回它的答案,即归的操作

(1)前序遍历

前序遍历的核心:前序位置的代码在刚刚进入一个二叉树节点的时候执行;

前序遍历OJ

https://leetcode.cn/problems/binary-tree-preorder-traversal/ 第一步确定递归函数的参数和返回值:

因为要打印前序遍历的节点值,因此第一个参数为 TreeNode root

每次打印之后我们都需要存储到List中,因此第二个参数为 List<Integer> result

返回void即可

代码语言:javascript
复制
public void preorder(TreeNode root,List<Integer> result)

第二步将原问题化为子问题

一棵二叉树的前序遍历结果 = 根节点 + 左子树的前序遍历结果 + 右子树的前序遍历结果。

第三步确定非边界条件:

我们想打印整棵树的节点值,即打印根节点+左子树+右子树的节点值即可

代码语言:javascript
复制
//根节点
result.add(root.val);
//左
preorder(root.left, result);
//右
preorder(root.right, result);

第四步确定边界条件:

当我们一直往里递时,总会有一个尽头,即节点为空时我们不再进行遍历打印,此时就开始归

代码语言:javascript
复制
if (root == null) {
    return;
}

整体代码:

代码语言:javascript
复制
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        preorder(root,result);
        return result;
    }

    public void preorder(TreeNode root,List<Integer> result) {
        //刚刚进入一个二叉树节点的时候执行;
        if (root == null) {
            return;
        }
        //根节点
        result.add(root.val);
        //左
        preorder(root.left, result);
        //右
        preorder(root.right, result);
    }
}

(2)中序遍历

中序遍历的核心:中序位置的代码在一个二叉树节点左子树都遍历完,即将开始遍历右子树的时候执行。 中序遍历OJ

https://leetcode.cn/problems/binary-tree-inorder-traversal/description/

代码语言:javascript
复制
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        preorder(root,result);
        return result;
    }

    public void preorder(TreeNode root,List<Integer> result) {
        if (root == null) {
            return;
        }
        //左
        preorder(root.left, result);
        //根节点
        result.add(root.val);
        //右
        preorder(root.right, result);
    }
}

(3)后序遍历

后续遍历的核心:后序位置的代码在将要离开一个二叉树节点的时候执行; 后序遍历OJ

https://leetcode.cn/problems/binary-tree-postorder-traversal/

代码语言:javascript
复制
class Solution {
     public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        inorder(root,result);
        return result;
    }
    public void inorder(TreeNode root,List<Integer> result) {
        //后序位置的代码在将要离开一个二叉树节点的时候执行;
        if(root == null) {
            return;
        }
        //左
        inorder(root.left,result);
        //右
        inorder(root.right,result);
        //中
        result.add(root.val);
    }
}

2.迭代实现

前言

为什么可以用迭代法(非递归的方式)来实现二叉树的前后中序遍历呢? 递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。

(1)前序遍历

方法一

充分利用栈的特点,前序遍历顺序:中-左-右,入栈顺序:中-右-左

代码语言:javascript
复制
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.right != null){
                stack.push(node.right);
            }
            if (node.left != null){
                stack.push(node.left);
            }
        }
        return result;
    }
}
方法二

根左右,刚进入节点就执行

思路:因为前序遍历的顺序为根左右,我们定义一个cur变量从根节点出发,沿着左子树依次入栈并打印,直到cur为空时停止,紧接着弹出当前节点(相当于递归中归的操作,回退到上一个节点进行判断)并记录判断当前节点是否有右子树 遍历左子树的循环条件为cur != null,只要cur不为空我们就一直循环入栈并打印的操作

遍历右子树的循环条件是什么呢?当我们走完遍历左子树的操作想要紧接着判断右子树,而且我们不知道右子树下面是否还存在一颗完整的树或左子树,因此我们要将两个循环写在一个大循环下

代码语言:javascript
复制
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        TreeNode cur = root;
        if (root == null){
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();

        while(cur != null || !stack.isEmpty()) {
            while(cur != null) {
                stack.push(cur);
                list.add(cur.val);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            cur = top.right;
        }
        return list;
    }

}

(2)中序遍历

方法一

中序遍历顺序: 左-中-右 入栈顺序: 左-右

代码语言:javascript
复制
// 中序遍历顺序: 左-中-右 入栈顺序: 左-右
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()){
           if (cur != null){
               stack.push(cur);
               cur = cur.left;
           }else{
               cur = stack.pop();
               result.add(cur.val);
               cur = cur.right;
           }
        }
        return result;
    }
}
方法二

左根右,左子树都遍历完再执行

代码语言:javascript
复制
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        TreeNode cur = root;
        if (root == null){
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();

        while(cur != null || !stack.isEmpty()) {
            while(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            list.add(top.val);
            cur = top.right;
        }
        return list;
    }
}

(3)后序遍历

方法一

后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果

代码语言:javascript
复制
// 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.left != null){
                stack.push(node.left);
            }
            if (node.right != null){
                stack.push(node.right);
            }
        }
        Collections.reverse(result);
        return result;
    }
}
方法二

左右根 将要离开一个二叉树节点的时候执行;

该方法需要在打印根节点前,标记根节点,防止重复打印; 当我们走else语句到达H时,进行下一次循环,cur != null,又将H进行了入栈,cur = cur.left,此时cur为空跳出while循环判断右子树,top为H,H的右子树为空,弹出H并打印,此时cur为空,没进入while循环,而top此时为D,D的右树不为空,又走了else将H入栈并判断造成了死循环,所以我们这里需要进行标记,打印过该节点就不再进行打印,直接判断上一个节点

代码语言:javascript
复制
class Solution {
     public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        inorder(root,result);
        return result;
    }
    public void inorder(TreeNode root,List<Integer> result) {
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode prev = null;
        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            if (top.right == null || top.right == prev) {
                //打印当前top 并且弹出
                stack.pop();
                result.add(top.val);
                prev = top;
            } else {
                cur = top.right;
            }
        }
    }
}

(4)层序遍历

层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。 需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。 思路:先将根节点入队,在出队的同时将当前节点的左右节点入队(需要判断左右节点是否存在)循环这一操作即可 二叉树层序遍历OJ

https://leetcode.cn/problems/binary-tree-level-order-traversal/description/

代码语言:javascript
复制
public List<List<Integer>> levelOrder(TreeNode root) {

        List<List<Integer>> ret = new ArrayList<List<Integer>>();
        if (root == null) {
            return ret; // 如果根节点为空,直接返回空的二维列表
        }
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root); // 先将根节点放入队列

        while (!queue.isEmpty()) {
            List<Integer> list = new ArrayList<>();
            int size = queue.size(); // 记录当前层的节点数

            for (int i = 0; i < size; i++) {
                TreeNode cur = queue.poll(); // 出队
               // list.add(cur.val); // 将当前节点的值加入当前层的列表中

                // 将当前节点的子节点(如果有的话)加入队列
                if (cur.left != null) {
                    queue.offer(cur.left);
                }
                if (cur.right != null) {
                    queue.offer(cur.right);
                }
            }

            // 将当前层的列表加入结果列表中
            ret.add(list);
        }

        return ret;
    }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.递归实现
    • 前言
    • (1)前序遍历
    • (2)中序遍历
    • (3)后序遍历
  • 2.迭代实现
    • 前言
    • (1)前序遍历
      • 方法一
      • 方法二
    • (2)中序遍历
      • 方法一
      • 方法二
    • (3)后序遍历
      • 方法一
      • 方法二
    • (4)层序遍历
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档