二叉树的前序、中序、后序、层序遍历,Java实现(递归、非递归)

关于二叉树的内容,我觉得二叉树最核心的地方就是它的几种遍历方式,基本上所有的问题都是围绕着几种遍历方式来的。因此就在这总结一下这几种遍历方式。

前序遍历

一、简介
前序遍历(DLR),是二叉树遍历的一种,也叫做先根遍历、先序遍历,可记做根左右。
前序遍历首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树。
若二叉树为空则结束返回,否则:
1.访问根结点
2.前序遍历左子树
2.前序遍历右子树
如图:前序遍历结果:ABDECF
在这里插入图片描述

二、代码实现
1.递归实现

class TreeNode{//TreeNode类,后边同样
    TreeNode left;
    TreeNode right;
    int val;
    TreeNode(int val){
        this.val = val;
    }
}

public class PreOrderTraversal {
    public void preOrder(TreeNode root){
        if(root != null){
            System.out.println(root.val);//打印根节点
            preOrder(root.left);//访问左子树
            preOrder(root.right);//访问右子树
        }
    }
}

2.非递归实现
方法一: 根节点存入栈中打印根节点,然后访问这个根节点的左子树,左子树也是将左子树的根存入栈中打印根节点,依次往下直到左子树为空,再取出栈顶元素,栈顶元素(访问完左子树的根节点)作为新的根节点去访问右子树。

public void preorderTraversal(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();//用一个栈来存放树中的节点
        while(root != null || !stack.isEmpty()) {//只要当前节点不为空(即当前节点的左右子树没有访问完毕)或者栈中还有节点(还有节点没有访问)
            while (root != null) {//一直往左走
                stack.push(root);//根节点入栈
                System.out.println(root.val);//打印根节点
                root = root.left;//访问左子树
            }
            root = stack.pop();//取出根节点
            root = root.right;//访问右子树
        }
    }

方法二: 我们知道前序遍历遵从根左右的顺序,所以我们打印根节点,并将它的右子树,左子树按照顺序压入栈中,每次只取出栈顶的一个节点,这样就可以保证所有的左子树都会在右子树之前取出并打印。

import java.util.Stack;

public class PreOrderTraversal {
    public void preorderTraversal(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        if(root == null)//树为空
            return;
        stack.add(root);//将根节点压入栈中
        while(!stack.isEmpty()){//只要栈不为空,执行循环
            TreeNode node = stack.pop();//取出栈顶元素打印,此时的栈顶元素是以node为根的子树的根
            System.out.println(node.val);
            if(node.right != null)//将右子树压入栈中
                stack.push(node.right);
            if(node.left != null)//将左子树压入栈中
                stack.push(node.left);
        }
    }
}

中序遍历

一、简介
中序遍历(LDR)是二叉树遍历的一种,也叫做中根遍历、中序周游。可记做左根右
中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。
若二叉树为空则结束返回,否则:
1.中序遍历左子树
2.访问根结点
3.中序遍历右子树
如图:中序遍历结果:DBEAFC
在这里插入图片描述
二、代码实现
1.递归实现

public class InOrderTraversal {
    public void inorderTraversal(TreeNode root){
        if(root != null){
            inorderTraversal(root.left);//访问左子树
            System.out.println(root.val);//打印根节点
            inorderTraversal(root.right);//访问右子树
        }
    }
}

2.非递归实现
根前序遍历方法一思路一样,不同的只是先不打印根节点,而是先访问左子树,再打印根节点,访问右子树。

import java.util.Stack;

public class InOrderTraversal {
    public void inorderTraversal(TreeNode root){
        Stack<TreeNode> stack = new Stack<>();
        while(root != null || !stack.isEmpty()){//只要当前节点不为空(即当前节点的左右子树没有访问完毕)或者栈中还有节点(还有节点没有访问)
            while(root != null){
                stack.push(root);//根节点入栈
                root = root.left;//访问左子树
            }
            root = stack.pop();//取出左子树的根节点
            System.out.println(root.val);//打印根节点
            root = root.right;//访问右子树
        }
    }
}

后序遍历

一、简介
首先遍历左子树,然后遍历右子树,最后遍历访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。
若二叉树为空则结束返回,否则:
1.中序遍历左子树
2.中序遍历右子树
3.访问根结点
如图:后序遍历结果:DEBFCA
在这里插入图片描述
二、代码实现
1.递归实现

public class PostOrderTraversal {
    public void postOrder(TreeNode root){
        if(root != null){
            postOrder(root.left);//访问左子树
            postOrder(root.right);//访问右子树
            System.out.println(root.val);//打印根节点
        }
    }
}

2.非递归实现
方法一: 常规遍历左右根,和前序、中序方法一类似。

import java.util.Stack;

public class PostOrderTraversal {
    public void postorderTraversal(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        TreeNode node = root;
        TreeNode prev = null;
        while(node != null || !stack.isEmpty()){
            while(node != null){
                //访问左子树
                stack.push(node);
                node = node.left;
            }
            //判断栈顶元素(根)
            node = stack.peek();
            //1.如果此时的根的右子树为空node.right == null
            //2.如果此时的根的右子树已经访问过了node.right == prev(prev记录的是上次访问打印的节点)
            if(node.right == null || node.right == prev){
                //打印根节点,并出栈,将打印过的节点从栈中删除
                stack.pop();
                System.out.println(node.val);
                //记录prev,表示以当前prev为根的子树已经访问过了
                prev = node;
                //node置null就不会再次访问以node为根节点的左右子树,这里的node既然已经打印,说明它的左右子树早已访问完毕
                node = null;
            }else{
                //访问右子树
                node = node.right;
            }
        }
    }
}

方法二: 和前序遍历的方法二类似,我们可以用一个线性表存放后序遍历的结果。我们知道后序遍历是左右根,但是我们可以反过来,一样先访问根,只是将根放在后边然后访问右子树放在根的前边左子树的后边,我们可以用线性表头插来实现这个操作,先访问根再右子树再左子树,只是每次都对线性表进行头插,这样最后的结果就还是左右根。

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

public class PostOrderTraversal {
    public List<Integer> postorderTraversal(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        List<Integer> list = new LinkedList<>();
        if(root == null)
            return list;
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            list.add(0, node.val);//头插此时根节点
            if(node.left != null)
                stack.push(node.left);
            if(node.right != null)
                stack.push(node.right);
        }
        return list;
    }
}

层序遍历

一、简介
除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
如图:层序遍历结果:ABCDEF
在这里插入图片描述
二、代码实现

import java.util.LinkedList;
import java.util.Queue;

public class LevelOrder {
    public void levelOrder(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        if(root != null){//如果根节点不为空,将第一层根节点入队列
            queue.offer(root);
        }
        while(!queue.isEmpty()){//只要队列不为空,执行循环
            int num = queue.size();//记录此时队列的长度,此时的num代表了某一层一共有多少个节点,防止被后边入队的节点个数影响输出这一层的节点
            for(int i = 0; i < num; i++){//对某一层的所有节点进行操作(从左到右)
                TreeNode topNode = queue.poll();//取出这一层第一个节点
                System.out.println(topNode.val);//打印节点
                if(topNode.left != null){//将此节点的左子树根节点入队列
                    queue.offer(topNode.left);
                }
                if(topNode.right != null){//将此节点的右子树根节点入队列
                    queue.offer(topNode.right);
                }
            }
        }
    }
}

总结:
所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问 题。 遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。一定要仔细体会其中的遍历过程。

  • 18
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值