二叉树遍历的几种方式

前言

本文主要是自己的一个学习记录,代码并未详细解释,如有错误或疑问欢迎留言讨论。

一. 依据完整中序创建二叉树

先创建二叉树,才方便验证下面的遍历方法是否正确。思想就是采用队列先进先出的方式,每个节点离开队列的同时,将其左右子节点加入队列中,直至队列中无节点。直接给出代码:
数据结构如下:

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    public TreeNode(int val) {
        this.val = val;
    }
}

创建方法如下:输入格式为:Integer[] list = {1, 2, 3, null, null, 6, 7}(LinkedList的用法见下一节预备知识)


public static TreeNode buildTree(Integer[] list) {
	if (list == null || list.length == 0 || list[0] == null)
	     return null;
	 TreeNode root = new TreeNode(list[0]);
	
	 int length = list.length;
	 LinkedList<TreeNode> link = new LinkedList<TreeNode>();
	 link.offer(root);
	 for (int i = 1; i < length; i += 2) {
	     TreeNode node = link.removeFirst();
	     Integer left = list[i];
	     Integer right = list[i + 1];
	     if (left != null)
	         node.left = new TreeNode(left);
	     else
	         node.left = null;
	     if (right != null)
	         node.right = new TreeNode(right);
	     else
	         node.right = null;
	
	     link.offer(node.left);
	     link.offer(node.right);
	 }
	 return root;
	
	}

二. 递归版前中后序遍历

递归版比较简单,此处直接给出代码:

// 递归方式先序遍历
    public static void pOrder(TreeNode root, List<Integer> list) {
        if (root == null)
            return;
        list.add(root.val);
        pOrder(root.left, list);
        pOrder(root.right, list);
    }

    // 递归方式中序遍历
    public static void iOrder(TreeNode root, List<Integer> list) {
        if (root == null)
            return;
        iOrder(root.left, list);
        list.add(root.val);
        iOrder(root.right, list);
    }

    // 递归方式后续遍历
    public static void aOrder(TreeNode root, List<Integer> list) {
        if (root == null)
            return;
        aOrder(root.left, list);
        aOrder(root.right, list);
        list.add(root.val);
    }

三. 预备知识:Java中的队列实现

Java的队列有两个常用实现类:LinkedList和ArrayDeque,其中LinkedList实现了List接口和Deque接口,ArrayDeque实现了Deque接口;而Deque是Queue的子接口。

Queue有如下方法:
在队尾添加元素:add(), offer()
在队头删除或获取元素:remove(), poll(), peek()<只获取不删除>,element()<只获取不删除>

Deuqe可以用作双端队列,还可以用作栈,下面列举几个Deque常用方法:
当作队列使用时的常用方法:
在开头添加元素:void addFirst(Object e), boolean offerFirst(Object e), 
在开头移除元素: Object removeFirst(),  Object pollFirst(),Object peekFirst()<获取但不删除第一个元素>,
在末尾添加元素:void addLast(Object e), boolean offerLast(Object e)
在末尾移除元素:Object removeLast(), Object pollLast(), Object peekLast()<获取但不删除最后一个元素>,
当作栈使用时的常用方法:
在栈顶添加元素:void push(Object e),
在栈顶移除元素:Object pop(Object e)
当然也包括Queue的方法。

四. 层次遍历

层次遍历的思想也是采用队列,先进先出,与第一节一模一样,下面给出代码:

// 层次遍历:采用队列的方式
    public static List<Integer> layerOrder(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        Deque<TreeNode> queue = new ArrayDeque<>();
        queue.addLast(root);
        while (queue != null && queue.size() > 0) {
            addLR(queue, list);
        }
        return list;
    }

    /**
     * 完成三个功能:添加左右节点;出队 队首节点;将队首节点添加进list
     * @param queue
     * @param list
     */
    static void addLR (Deque<TreeNode> queue, List<Integer> list) {
        TreeNode currentNode = queue.removeFirst();
        if (currentNode != null) {
            list.add(currentNode.val);
            if (currentNode.left != null)
                queue.addLast(currentNode.left);
            if (currentNode.right != null)
                queue.addLast(currentNode.right);
        }
    }

五. 非递归版前中后序遍历

一般来说,递归的非递归实现都是用栈实现的,仔细想一下递归的过程,就能得出栈实现的程序。

  /**
     * 前序
     *思想:记录元素入栈顺序;先进行入栈操作,将每个左节点入栈;然后出栈,出栈时将右子树根节点入栈。
     */
    public static List<Integer> preOrderStack(TreeNode root) {
        if (root == null)
            return null;
        /**
         * 初始化栈和队列;
         */
        List<Integer> list = new ArrayList<>();
        Deque<TreeNode> stack = new LinkedList<>();
        stack.add(root);
        list.add(root.val);
        TreeNode currentNode = null;
        while (stack.size() > 0) {
            //入栈,一直将左子树根节点入栈
            currentNode = stack.peekFirst().left; // 获取栈顶元素的左子树节点
            while (currentNode != null) {
                stack.push(currentNode);
                list.add(currentNode.val); // 记录入栈节点值
                currentNode = currentNode.left;
            }

            // 出栈栈顶元素,并将右子树根节点入栈;
            // 需要说明的是,出栈的目的是为了将右子树根节点入栈,所以此步必须要真正入栈一个节点
            while (currentNode == null && stack.size() > 0) {
                currentNode = stack.pop().right;
            }
            if (currentNode != null) {
                list.add(currentNode.val); // 记录入栈节点值
                stack.push(currentNode);
            }
        }
        return list;
    }

    /**
     * 中序;
     * 思想:出入栈顺序与前序遍历相同,不同的是中序在出栈的时候记录元素值。
     * @param root
     * @return
     */
    public static List<Integer> inOrderStack(TreeNode root) {
        if (root == null)
            return null;

        List<Integer> list = new ArrayList<>();
        Deque<TreeNode> stack = new LinkedList<>();
        stack.push(root);
        TreeNode currentNode = root;
        while (stack.size() > 0){
            // 入栈左子树
            currentNode = stack.peekFirst().left;
            while (currentNode != null) {
                stack.push(currentNode);
                currentNode = currentNode.left;
            }
            // 出栈,入栈右子树
            while (currentNode == null && stack.size() > 0) {
                currentNode = stack.pop();
                list.add(currentNode.val); // 记录出栈节点值
                currentNode = currentNode.right;
            }
            if (currentNode != null)
                stack.push(currentNode);
        }

        return list;
    }

    /**
     * 后序
     * 思想:还是先入左子树根节点再入右子树根节点,出栈时记录;
     * 需要注意的是:对于一颗树来说,左结点和右节点都不存在或者出栈了才进行出栈操作。
     * @param root
     * @return
     */
    public static List<Integer> afterOrderStack(TreeNode root) {
        if (root == null) {
            return null;
        }

        List<Integer> list = new ArrayList<>();
        Deque<TreeNode> stack = new LinkedList<>();
        stack.add(root);
        TreeNode currentNode = null;
        while (stack.size() > 0) {
            // 入栈左子树
            currentNode = stack.getFirst().left;
            stack.getFirst().left = null; // 断掉与左结点的联系
            while (currentNode != null) {
                stack.push(currentNode);
                currentNode = currentNode.left;
                stack.getFirst().left = null; // 断掉与左结点的联系
            }
            // 入栈右子树
            currentNode = stack.getFirst().right;
            stack.getFirst().right = null; // 断掉与右节点的联系
            if (currentNode != null) {
                stack.push(currentNode);
            }
            else { // 左右节点都为null,出栈
                list.add(stack.pop().val);
            }
        }
        return list;
    }

总结

理解递归具体的运行过程后再实现非递归方式就会游刃有余了。本文写的简陋,如有错误欢迎指出。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值