二叉树的遍历(代码实现+详细注释)

层级遍历

代码实现的每一步解释

public List<T> levelOrder(){
    List<T> list = new LinkedList<>();
    MyArrayQueue<Node> queue1 = new MyArrayQueue<>();
    
    queue1.enQueue(root); // 将根节点入队列
    
    while (!queue1.isEmpty()){
        Node node = queue1.deQueue(); // 从队列中取出一个节点
        list.add(node.value); // 将节点的值添加到结果列表
        
        if(node.left != null){
            queue1.enQueue(node.left); // 左孩子入队列
        }
        if(node.right != null){
            queue1.enQueue(node.right); // 右孩子入队列
        }
    }
    return list; // 返回结果列表
}

这个代码实现了层级遍历,通过使用队列来记录待处理的节点,从而逐层遍历整个二叉树。

上面的层级遍历算法实现了广度优先搜索 (BFS),并使用了队列 (Queue) 数据结构。具体步骤如下:

  1. 初始化队列:创建一个空队列并将根节点入队列。
  2. 循环遍历:当队列不为空时,重复以下步骤:
    • 从队列中取出一个节点。
    • 将该节点的值添加到结果列表中。
    • 如果该节点的左子节点不为空,则将左子节点入队列。
    • 如果该节点的右子节点不为空,则将右子节点入队列。

我们可以用一个简单的二叉树作为例子:

        A
       / \
      B   C
     / \   \
    D   E   F

让我们一步一步地看这个算法是如何工作的:

初始化

  • 根节点 A 入队列。
  • 队列: [A]

循环遍历

  1. 从队列中取出 A,将 A 的值添加到结果列表,左右孩子 BC 入队列。

    • 结果列表: [A]
    • 队列: [B, C]
  2. 从队列中取出 B,将 B 的值添加到结果列表,左右孩子 DE 入队列。

    • 结果列表: [A, B]
    • 队列: [C, D, E]
  3. 从队列中取出 C,将 C 的值添加到结果列表,右孩子 F 入队列。

    • 结果列表: [A, B, C]
    • 队列: [D, E, F]
  4. 从队列中取出 D,将 D 的值添加到结果列表,没有孩子。

    • 结果列表: [A, B, C, D]
    • 队列: [E, F]
  5. 从队列中取出 E,将 E 的值添加到结果列表,没有孩子。

    • 结果列表: [A, B, C, D, E]
    • 队列: [F]
  6. 从队列中取出 F,将 F 的值添加到结果列表,没有孩子。

    • 结果列表: [A, B, C, D, E, F]
    • 队列: []

队列为空,算法结束,最终结果列表为 [A, B, C, D, E, F]

后序遍历

代码实现的每一步解释

public List<T> postOrder(){
    List<T> list = new LinkedList<>(); // 存储遍历结果的容器

    MyArrayStack<Node> nodeMyArrayStack = new MyArrayStack<>(); // 创建一个栈
    nodeMyArrayStack.push(root); // 将根节点入栈

    while(!nodeMyArrayStack.isEmpty()){
        Node pop = nodeMyArrayStack.pop(); // 从栈中取出一个节点
        list.add(0, pop.value); // 使用头插法将节点的值插入到结果列表的开头

        if(pop.left != null){
            nodeMyArrayStack.push(pop.left); // 左孩子入栈
        }
        if(pop.right != null){
            nodeMyArrayStack.push(pop.right); // 右孩子入栈
        }
    }
    return list; // 返回结果列表
}

这个代码实现了二叉树的后序遍历,通过使用栈和头插法来确保节点值按后序遍历的顺序排列。
这段代码实现了二叉树的后序遍历(Post-order Traversal),但与传统的递归方法不同,这里使用了栈 (Stack) 数据结构来进行非递归的后序遍历。后序遍历的顺序是:先访问左子树,再访问右子树,最后访问根节点。

代码的具体步骤如下:

  1. 初始化栈:创建一个空栈并将根节点入栈。
  2. 循环遍历:当栈不为空时,重复以下步骤:
    • 从栈中取出一个节点。
    • 将该节点的值插入到结果列表的开头(使用头插法)。
    • 如果该节点的左子节点不为空,则将左子节点入栈。
    • 如果该节点的右子节点不为空,则将右子节点入栈。

让我们用一个简单的二叉树作为例子来说明这个算法:

        A
       / \
      B   C
     / \   \
    D   E   F

初始化

  • 根节点 A 入栈。
  • 栈: [A]
  • 结果列表: []

循环遍历

  1. 从栈中取出 A,将 A 的值插入到结果列表的开头,左右孩子 BC 入栈。

    • 结果列表: [A]
    • 栈: [B, C]
  2. 从栈中取出 C,将 C 的值插入到结果列表的开头,右孩子 F 入栈。

    • 结果列表: [C, A]
    • 栈: [B, F]
  3. 从栈中取出 F,将 F 的值插入到结果列表的开头,没有孩子。

    • 结果列表: [F, C, A]
    • 栈: [B]
  4. 从栈中取出 B,将 B 的值插入到结果列表的开头,左右孩子 DE 入栈。

    • 结果列表: [B, F, C, A]
    • 栈: [D, E]
  5. 从栈中取出 E,将 E 的值插入到结果列表的开头,没有孩子。

    • 结果列表: [E, B, F, C, A]
    • 栈: [D]
  6. 从栈中取出 D,将 D 的值插入到结果列表的开头,没有孩子。

    • 结果列表: [D, E, B, F, C, A]
    • 栈: []

栈为空,算法结束,最终结果列表为 [D, E, B, F, C, A],这就是后序遍历的顺序。

后序遍历(递归实现)

递归是一种解决问题的编程技术,它涉及一个函数调用自身,以便逐步解决问题的各个子部分。在你的代码中,postOrder2方法使用递归来实现二叉树的后序遍历。让我们一步步来分析它是如何工作的。

递归后序遍历的执行步骤

后序遍历的顺序是:左子树 -> 右子树 -> 根节点。下面是详细的步骤:

  1. 递归出口:如果当前节点 (node) 为空,直接返回。这是递归的结束条件,确保我们在遇到叶子节点的孩子(即 null)时不再继续递归。

  2. 递归调用:对于每个节点,先递归遍历其左子树,然后递归遍历其右子树,最后处理当前节点(将其值添加到列表中)。

我们用一个简单的二叉树作为例子来说明这个算法:

        A
       / \
      B   C
     / \   \
    D   E   F

代码实现的每一步解释

private void postOrder2(Node node, List<T> list){
    if (node == null){
        return; // 递归出口
    }
    postOrder2(node.left, list); // 遍历左子树
    postOrder2(node.right, list); // 遍历右子树
    list.add(node.value); // 处理当前节点
}

public List<T> postOrder2(){
    LinkedList<T> list = new LinkedList<>();
    postOrder2(root, list); // 从根节点开始递归
    return list; // 返回结果列表
}

递归过程示例

  1. 初始调用

    • postOrder2(root, list),其中 root 是节点 A
  2. 递归遍历左子树

    • A 的左子节点 B 调用 postOrder2(B, list)
  3. 递归遍历左子树的左子树

    • B 的左子节点 D 调用 postOrder2(D, list)
    • D 没有左子树和右子树,直接将 D 添加到列表:list = [D]
  4. 递归遍历左子树的右子树

    • B 的右子节点 E 调用 postOrder2(E, list)
    • E 没有左子树和右子树,直接将 E 添加到列表:list = [D, E]
  5. 处理左子树的根节点

    • B 添加到列表:list = [D, E, B]
  6. 递归遍历右子树

    • A 的右子节点 C 调用 postOrder2(C, list)
  7. 递归遍历右子树的左子树

    • C 没有左子树,跳过。
  8. 递归遍历右子树的右子树

    • C 的右子节点 F 调用 postOrder2(F, list)
    • F 没有左子树和右子树,直接将 F 添加到列表:list = [D, E, B, F]
  9. 处理右子树的根节点

    • C 添加到列表:list = [D, E, B, F, C]
  10. 处理根节点

    • A 添加到列表:list = [D, E, B, F, C, A]

递归的关键点

  • 递归出口:确保递归终止。
  • 递归调用:逐步处理左子树和右子树,再处理根节点。
  • 头插法:在处理根节点时将节点值添加到列表的末尾。

通过递归调用和递归出口的配合,递归函数能够遍历整个二叉树并按照后序遍历的顺序处理每个节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值