[数据结构与算法]-二叉树的先序、中序、后序遍历的方法及其递归与非递归的实现(Java)

版权声明:本文为博主原创文章,转载时请注明出处,谢谢!喝酒不骑马 邮箱[email protected] https://blog.csdn.net/Colton_Null/article/details/80710273

本文欢迎转载,转载前请联系作者,经允许后方可转载。转载后请注明出处,谢谢! http://blog.csdn.net/colton_null 作者:喝酒不骑马 Colton_Null from CSDN


一.先序、中序、后续遍历简介

对于列表结构的数据,我们很容易就能通过for循环遍历它。那么对于树形结构的数据,我们如何遍历它呢?
本文介绍有关二叉树的遍历方法,主要有三种——先序、中序、后续遍历。

1.先序遍历

先序遍历是最好理解的一种遍历方法,可以想象它为顺腾摸瓜,摸到一个节点就直接访问它。那么怎么摸呢?先摸根节点,再摸左节点,最后摸右节点。在摸左右节点的时候,如果子节点还有左或右节点,则还要优先访问左节点,依次类推。即根-左-右。

2.中序遍历

中序遍历是先遍历左节点,再访问根节点,再遍历右节点。即左-根-右。

3.后续遍历

后续遍历为先遍历左节点,再遍历右节点,最终读根节点。即左-右-根。

总结:

  • 先序遍历:根-左-右。
  • 中序遍历:左-根-右。
  • 后序遍历:左-右-根。

二.遍历实践

给定一个二叉树如下图所示。
这里写图片描述

1.先序遍历过程

先读根节点10,然后访问左节点,左节点存在,则读左节点5。然后继续访问5的左节点,读节点3,然后读节点1。此时,1没有左节点,则访问1的右节点,同样1也没有右节点,则回到节点3。继续访问3的右节点,存在,则读4……
最后结果是:10 5 3 1 4 7 8 15 13 11 20 30

2.中序遍历过程

访问根节点10,先不读10,看10有没有左节点,有则访问5。同样不读节点5,看它有没有左节点……最后访问到节点1。节点1没有左节点,则读根节点即1本身,然后访问它的右节点。发现没有右节点,则回到节点5,读节点5,然后访问它的右节点……。
最后结果是:1 3 4 5 7 8 10 11 13 15 20 30

3.后序遍历过程

访问根节点10,读其左节点,存在,则继续读左节点……最后访问到节点1,继续访问节点1的左节点,不存在,则继续访问节点1的右节点,也不存在,所以最终读取节点1。然后回到节点3,访问其右节点4。同样右节点为树叶,则读取4,回到节点3,读取节点3,然后回到节点5,访问其右节点……
最后结果是:1 4 3 8 7 5 11 13 30 20 15 10

三.递归法实现三种遍历(Java)

节点类的定义
BinaryNode.java

public class BinaryNode {
    // 节点元素
    private Object element;
    // 左节点
    private BinaryNode left;
    // 右节点
    private BinaryNode right;

    public Object getElement() {
        return element;
    }

    public void setElement(Object element) {
        this.element = element;
    }

    public BinaryNode getLeft() {
        return left;
    }

    public void setLeft(BinaryNode left) {
        this.left = left;
    }

    public BinaryNode getRight() {
        return right;
    }

    public void setRight(BinaryNode right) {
        this.right = right;
    }
}

1.先序遍历

/**
 * 先序遍历打印二叉树(递归)
 *
 * @param rootNode
 */
public void printTreeByPreorderTraversal(BinaryNode rootNode) {
    // 先输出根节点
    System.out.print(rootNode.getElement().toString() + " ");
    if (rootNode.getLeft() != null) {
        // 访问左节点
        printTreeByPreorderTraversal(rootNode.getLeft());
    }
    if (rootNode.getRight() != null) {
        // 再访问右节点
        printTreeByPreorderTraversal(rootNode.getRight());
    }
}

2.中序遍历

/**
 * 中序遍历打印二叉树(递归)
 *
 * @param rootNode
 */
public void printTreeByInorderTraversal(BinaryNode rootNode) {
    if (rootNode.getLeft() != null) {
        // 先访问左节点
        printTreeByInorderTraversal(rootNode.getLeft());
    }
    // 输入根节点
    System.out.print(rootNode.getElement().toString() + " ");
    if (rootNode.getRight() != null) {
        // 再访问右节点
        printTreeByInorderTraversal(rootNode.getRight());
    }
}

3.后序遍历

/**
 * 后续遍历打印二叉树(递归)
 *
 * @param rootNode
 */
public void printTreeByPostorderTraversal(BinaryNode rootNode) {
    if (rootNode.getLeft() != null) {
        // 先访问左节点
        printTreeByPostorderTraversal(rootNode.getLeft());
    }
    if (rootNode.getRight() != null) {
        // 再访问右节点
        printTreeByPostorderTraversal(rootNode.getRight());
    }
    // 输出根节点
    System.out.print(rootNode.getElement().toString() + " ");
}

四.非递归法实现三种遍历(Java)

1.先序遍历

用栈,将节点压栈,先访问左节点,访问完左节点后出栈,访问右节点。

/**
 * 先序遍历打印二叉树(堆栈)
 *
 * @param rootNode
 */
public void printTreeByPreorderTraversalStack(BinaryNode rootNode) {
    Stack<BinaryNode> stack = new Stack<>();
    while (rootNode != null || stack.size() > 0) {
        if (rootNode != null) {
            stack.push(rootNode);
            System.out.print(rootNode.getElement().toString() + " ");
            rootNode = rootNode.getLeft();
        } else {
            rootNode = stack.pop();
            rootNode = rootNode.getRight();
        }
    }
}

2.中序遍历

/**
 * 中序遍历打印二叉树(堆栈)
 * @param rootNode
 */
public void printTreeByInorderTraversalStack(BinaryNode rootNode) {
    Stack<BinaryNode> stack = new Stack<>();
    while(rootNode != null || stack.size() > 0) {
        if(rootNode != null) {
            stack.push(rootNode);
            rootNode = rootNode.getLeft();
        } else {
            rootNode = stack.pop();
            System.out.print(rootNode.getElement().toString() + " ");
            rootNode = rootNode.getRight();
        }
    }
}

3.后序遍历

后续遍历麻烦一些。主要难点在于要左右节点都访问完后才能读取根节点。这里用两个栈来完成后序遍历过程。一个栈用于遍历,另一个栈用于记录节点的访问顺序。由于栈先进后出,所以用这种方法(两个栈)需要先记录根节点,然后遍历右节点并记录,最后遍历左节点。

/**
 * 后续遍历打印二叉树(堆栈)
 * 用两个堆栈实现
 *
 * @param rootNode
 */
public void printTreeByPostorderTraversalWith2Stacks(BinaryNode rootNode) {
    // 用于遍历
    Stack<BinaryNode> stack = new Stack<>();
    // 用于记录输出的堆栈
    Stack<BinaryNode> output = new Stack<>();
    while (rootNode != null || stack.size() > 0) {
        if (rootNode != null) {
            stack.push(rootNode);
            output.push(rootNode);
            // 先找右节点
            rootNode = rootNode.getRight();
        } else {
            rootNode = stack.pop();
            rootNode = rootNode.getLeft();
        }
    }
    while (output.size() > 0) {
        System.out.print(output.pop().getElement().toString() + " ");
    }
}

以上就是有关先序、中序、后序遍历的方法以及其Java实现。


有关[数据结构与算法]的学习内容已经上传到github,喜欢的朋友可以支持一下。
https://github.com/MaYuzhe/data-structures-and-algorithm-study-notes-java


站在前人的肩膀上前行,感谢以下博客及文献的支持。

猜你喜欢

转载自blog.csdn.net/Colton_Null/article/details/80710273
今日推荐