图解数据结构(06) -- 二叉树的遍历

1、二叉树的遍历

在计算机程序中,遍历本身是一个线性操作,所以遍历同样具有线性结构的数组或链表是一件轻而易举的事情,如下图:
在这里插入图片描述
二叉树则是典型的非线性数据结构,遍历时需要把非线性关联的节点转化成一个线性的序列,以不同的方式来遍历,遍历出的序列顺序也不同:
在这里插入图片描述

二叉树遍历分类

从节点之间位置关系的角度来看,二叉树的遍历分为4种:

  • 前序遍历
  • 中序遍历
  • 后序遍历
  • 层序遍历

进一步可分为两大类:

  • 深度优先遍历(前序遍历、中序遍历、后序遍历)
  • 广度优先遍历(层序遍历)

2、深度优先遍历

深度优先和广度优先这两个概念不止局限于二叉树,它们更是一种抽象的算法思想,决定了访问某些复杂数据结构的顺序,深度优先:就是偏向于纵深,“一头扎到底”的访问方式!

【1】前序遍历

二叉树的前序遍历,输出顺序是根节点、左子树、右子树,如下图就是一个二叉树的前序遍历:
在这里插入图片描述
每个节点左侧的序号代表该节点的输出顺 序,详细步骤如下:

  • 首先输出的是根节点1
    在这里插入图片描述
  • 由于根节点1存在左孩子,输出左孩子节点2
    在这里插入图片描述
  • 由于节点2也存在左孩子,输出左孩子节点4
    在这里插入图片描述
  • 节点4既没有左孩子,也没有右孩子,那么回到节点2,输出节点2的右孩子节点5
    在这里插入图片描述
  • 节点5既没有左孩子,也没有右孩子,那么回到节点1,输出节点1的右孩子节点3
    在这里插入图片描述
  • 节点3没有左孩子,但是有右孩子,因此输出节点3的右孩子节点6
    在这里插入图片描述
    到此为止,所有的节点都遍历输出完毕

【2】中序遍历

二叉树的中序遍历,输出顺序是左子树、根节点、右子树,如下图就是一个二叉树的中序遍历:
在这里插入图片描述
每个节点左侧的序号代表该节点的输出顺 序,详细步骤如下:

  • 首先访问根节点的左孩子,如果这个左孩子还拥有左孩子,则继续深入访问下去,一直找到不再有左孩子的节点,并输出该节点。显然,第一个没有左孩子的节点是节点4
    在这里插入图片描述
  • 依照中序遍历的次序,接下来输出节点4的父节点2
    在这里插入图片描述
  • 再输出节点2的右孩子节点5
    在这里插入图片描述
  • 以节点2为根的左子树已经输出完毕,这时再输出整个二叉树的根节点1
    在这里插入图片描述
  • 由于节点3没有左孩子,所以直接输出根节点1的右孩子节点3
    在这里插入图片描述
  • 最后输出节点3的右孩子节点6
    在这里插入图片描述
    到此为止,所有的节点都遍历输出完毕

【3】后序遍历

二叉树的后序遍历,输出顺序是左子树、右子树、根节点,下图就是一个二叉树的后序遍历在这里插入图片描述
后序遍历的核心思想同上,在此不再赘述,具体实现代码如下:

//构建二叉树
public class TreeNode2 {
    public static TreeNode createBinaryTree(LinkedList<Integer>inputList){
        TreeNode node = null;
        if(inputList==null || inputList.isEmpty()){
            return null;
        }
        Integer data = inputList.removeFirst();
        if(data != null){
            node = new TreeNode(data);
            node.leftChild = createBinaryTree(inputList);
            node.rightChild = createBinaryTree(inputList);
        }
        return node;
    }
    //二叉树前序遍历
    public static void preOrderTraveral(TreeNode node){
        if(node == null){
            return;
        }
        System.out.println(node.data);
        preOrderTraveral(node.leftChild);
        preOrderTraveral(node.rightChild);
    }

    //二叉树中序遍历
    public static void inOrderTraveral(TreeNode node){
        if(node == null){
            return;
        }
        inOrderTraveral(node.leftChild);
        System.out.println(node.data);
        inOrderTraveral(node.rightChild);
    }

    //二叉树后序遍历
    public static void postOrderTraveral(TreeNode node){
        if(node == null){
            return;
        }
        postOrderTraveral(node.leftChild);
        postOrderTraveral(node.rightChild);
        System.out.println(node.data);
    }
    // 二叉树节点
    private static class TreeNode {
        int data;
        TreeNode leftChild;
        TreeNode rightChild;
        TreeNode(int data) {
            this.data = data;
        }
    }

    public static void main(String[] args) {
        LinkedList<Integer> inputList = new LinkedList<Integer>(Arrays.asList(new Integer[]{3,2,9,null,null,10,null,null,8,null,4}));
        TreeNode treeNode = createBinaryTree(inputList);
        System.out.println(" 前序遍历:");
        preOrderTraveral(treeNode);
        System.out.println(" 中序遍历:");
        inOrderTraveral(treeNode);
        System.out.println(" 后序遍历:");
        postOrderTraveral(treeNode);
    }

编译输出:
在这里插入图片描述
这3种遍历方式的区别:仅仅是输出的执行位置不同:前序遍历的输出在前,中序遍历的输出在中间,后序遍历的输出在最后;在代码的main函数中,通过{3,2,9,null,null,10,null,null,8,null,4}这 样一个线性序列,构建成的二叉树如下:
在这里插入图片描述

【4】栈实现二叉树的非递归遍历

绝大多数可以用递归解决的问题都可以用 ‘栈’ 来解决,因为递归和栈都有回溯的特性;
下面以二叉树的前序遍历为例展示具体过程:

  • 1. 首先遍历二叉树的根节点1,放入栈中
    在这里插入图片描述
  • 2. 遍历根节点1的左孩子节点2,放入栈中
    在这里插入图片描述
  • 3. 遍历节点2的左孩子节点4,放入栈中
    在这里插入图片描述
  • 节点4既没有左孩子,也没有右孩子,需要回溯到上一个节点2。可是现在并不是做递归操作,栈已经存储了刚才遍历的路径;让旧的栈顶元素4出栈,就可以重新访问节点2,得到节点2的右孩子节点5;此时节点2已经没有利用价值(已经访问过左孩子和右孩子),节点2出栈,节点5入栈
    在这里插入图片描述
  • 节点5既没有左孩子,也没有右孩子,需要再次回溯,一直回溯到节点 1;所以让节点5出栈,根节点1的右孩子是节点3,节点1出栈,节点3入栈
    在这里插入图片描述
  • 6. 节点3的右孩子是节点6,节点3出栈,节点6入栈
    在这里插入图片描述
  • 7.节点6既没有左孩子,也没有右孩子,所以节点6出栈。此时栈为空,遍历结束
    在这里插入图片描述
    —————————————————————————————————————————————
    内容来源:《漫画算法》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值