二叉树的创建,以及递归方式遍历和非递归方式遍历(Java)

二叉树的遍历方式主要分为两类:深度优先遍历和广度优先遍历,前者包括前序遍历,中序遍历和后序遍历;后者主要指层序遍历。这篇文章中,我们先对二叉树的创建和每个遍历过程的思路进行分析,然后在文章最后给出一个完整的代码供参考。

目录

二叉树的创建

前序遍历(递归方式)

前序遍历(非递归)

中序遍历(非递归)

后序遍历(非递归)

层序遍历(非递归)

完整代码


二叉树的创建

我们这里用前序序列创建二叉树。

思路:

  1. 用双向链表存放节点值,包括叶子null节点(可见后面的完整代码部分)
  2. 先创建根节点,然后以递归方式依次创建左子树和右子树
  3. 遇到空值(叶子null节点)时,返回null

我们在用递归方式构建二叉树时,最好将每个递归的过程都写在一个框内,方便梳理调用过程。

    //以前序方式创建二叉树
    //递归实现,将每一个递归的过程写在框中,以便梳理过程
    public static TreeNode createBinaryTree(LinkedList<Integer> inputList){
        TreeNode node = null;
        if(inputList == null || inputList.isEmpty()){
            return null;
        }
        Integer data = inputList.removeFirst();//返回LinkedList的第一个元素,并将头指针移动到下一个节点。
        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.print(node.data + " ");
        preOrderTraveral(node.leftChild);
        preOrderTraveral(node.rightChild);
    }

前序遍历(非递归)

用非递归的方式进行二叉树的遍历,我们可以用栈来存储二叉树的节点,然后打印输出。

思路:

先访问根节点,然后依次访问最左侧的节点,并压入栈中,如果没有左节点则进行弹栈操作,并指向弹出节点的右节点。

//非递归方式前序遍历
    /*
    思路:
    先访问根节点,若左节点非空,用指针则顺次访问左节点,并压入栈,直到左节点为空;判断栈是否为空,若栈非空,则弹出一个节点,并用指针指向该节点的右节点,重复操作。
     */
    public static void preOrder(TreeNode root){
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode temp = root;
        while(temp != null || !stack.isEmpty()){
            //节点非空时,打印输出并入栈
            while(temp != null){
                System.out.print(temp.data + " ");
                stack.push(temp);
                temp = temp.leftChild;
            }
            //如果节点没有左孩子,则弹出栈顶节点,并访问节点右孩子
            if(!stack.isEmpty()){
                temp = stack.pop();
                temp = temp.rightChild;
            }
        }
    }

中序遍历(非递归)

思路和前序遍历一样,我们只需更改打印输出的位置即可。

//非递归中序遍历
    /*
    思路:
    先遍历一遍最左边的节点,依次压入栈中,左节点为空时弹栈,并输出,然后将指针指向右节点。
     */
    public static void inOrder(TreeNode root){
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode temp = root;
        while(temp != null || !stack.isEmpty()){
            while(temp != null){
                stack.push(temp);
                temp = temp.leftChild;
            }
            if(!stack.isEmpty()){
                temp = stack.pop();
                System.out.print(temp.data + " ");
                temp = temp.rightChild;
            }
        }
    }

后序遍历(非递归)

后序遍历有点麻烦,我们需要采用逆向思维,用两个栈来进行操作,用第一个栈来压入根节点,并弹出,然后依次压入左右节点,再弹出一个节点,重复操作,每次弹出的节点放入第二个栈来进行输出,最终得到后序序列。

//非递归后序遍历
    /*
    思路:
    后序的遍历顺序为左右根,若我们用一个栈按照根右左的顺序弹出节点,同时放入另一个栈中,那么后一个栈的弹出顺序就是正确的后序顺序
    于是:我们将根节点压入栈,然后弹出根节点,再依次将左右节点压入栈中,重复操作;用另一个栈存放每次弹出的节点,然后输出后一个栈即可。
     */
    public static void postOrder(TreeNode root){
        Stack<TreeNode> stack1 = new Stack<TreeNode>();
        Stack<TreeNode> stack2 = new Stack<TreeNode>();
        TreeNode temp = root;
        if(temp != null)
            stack1.push(temp);
        while(!stack1.isEmpty()){
            temp = stack1.pop();
            stack2.push(temp);
            if(temp.leftChild != null)
                stack1.push(temp.leftChild);
            if(temp.rightChild != null)
                stack1.push(temp.rightChild);
        }
        while(!stack2.isEmpty()){
            temp = stack2.pop();
            System.out.print(temp.data +" ");
        }
    }

层序遍历(非递归)

层序遍历是按层进行遍历,一层一层往下,由此可以使用先进先出的线性结构:队列。

思路:根节点入队列,然后出队列,并输出,同时其左节点和右节点依次入队列,重复操作。要注意的是,入队我们使用方法offer(),而不是add(),避免了队满时抛异常的情况;同理,出队我们使用方法poll(),而不是remove(),避免了队空时抛异常的情况。

//层序遍历
    public static void levelOrder(TreeNode root){
        //LinkedList类实现了Deque接口,而Deque继承自Queue接口,因此LinkedList可以当成Queue来用
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        TreeNode temp = root;;
        queue.offer(temp);//offer()方法与add()方法类似,但是当队列满时,不像add()一样抛出异常,而是返回false
        while(!queue.isEmpty()){
            temp = queue.poll();//poll()方法与remove()方法都是移除队首元素,但是当队列为空时,poll()返回null
            System.out.print(temp.data + " ");
            if(temp.leftChild != null)
                queue.offer(temp.leftChild);
            if(temp.rightChild != null)
                queue.offer(temp.rightChild);
        }
    }

完整代码

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Stack;

public class MyBinaryTree {
    private static class TreeNode {
        int data;
        TreeNode leftChild;
        TreeNode rightChild;
        TreeNode(int data){
            this.data = data;
        }
    }

    //以前序方式创建二叉树
    //递归实现,将每一个递归的过程写在框中,以便梳理过程
    public static TreeNode createBinaryTree(LinkedList<Integer> inputList){
        TreeNode node = null;
        if(inputList == null || inputList.isEmpty()){
            return null;
        }
        Integer data = inputList.removeFirst();//返回LinkedList的第一个元素,并将头指针移动到下一个节点。
        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.print(node.data + " ");
        preOrderTraveral(node.leftChild);
        preOrderTraveral(node.rightChild);
    }

    //中序遍历
    public static void inOrderTraveral(TreeNode node){
        if(node == null)
            return;
        inOrderTraveral(node.leftChild);
        System.out.print(node.data + " ");
        inOrderTraveral(node.rightChild);
    }

    //后序遍历
    public static void postOrderTraveral(TreeNode node){
        if(node == null)
            return;
        postOrderTraveral(node.leftChild);
        postOrderTraveral(node.rightChild);
        System.out.print(node.data + " ");
    }

    //非递归方式前序遍历
    /*
    思路:
    先访问根节点,若左节点非空,用指针则顺次访问左节点,并压入栈,直到左节点为空;判断栈是否为空,若栈非空,则弹出一个节点,并用指针指向该节点的右节点,重复操作。
     */
    public static void preOrder(TreeNode root){
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode temp = root;
        while(temp != null || !stack.isEmpty()){
            //节点非空时,打印输出并入栈
            while(temp != null){
                System.out.print(temp.data + " ");
                stack.push(temp);
                temp = temp.leftChild;
            }
            //如果节点没有左孩子,则弹出栈顶节点,并访问节点右孩子
            if(!stack.isEmpty()){
                temp = stack.pop();
                temp = temp.rightChild;
            }
        }
    }

    //非递归中序遍历
    /*
    思路:
    先遍历一遍最左边的节点,依次压入栈中,左节点为空时弹栈,并输出,然后将指针指向右节点。
     */
    public static void inOrder(TreeNode root){
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode temp = root;
        while(temp != null || !stack.isEmpty()){
            while(temp != null){
                stack.push(temp);
                temp = temp.leftChild;
            }
            if(!stack.isEmpty()){
                temp = stack.pop();
                System.out.print(temp.data + " ");
                temp = temp.rightChild;
            }
        }
    }

    //非递归后序遍历
    /*
    思路:
    后序的遍历顺序为左右根,若我们用一个栈按照根右左的顺序弹出节点,同时放入另一个栈中,那么后一个栈的弹出顺序就是正确的后序顺序
    于是:我们将根节点压入栈,然后弹出根节点,再依次将左右节点压入栈中,重复操作;用另一个栈存放每次弹出的节点,然后输出后一个栈即可。
     */
    public static void postOrder(TreeNode root){
        Stack<TreeNode> stack1 = new Stack<TreeNode>();
        Stack<TreeNode> stack2 = new Stack<TreeNode>();
        TreeNode temp = root;
        if(temp != null)
            stack1.push(temp);
        while(!stack1.isEmpty()){
            temp = stack1.pop();
            stack2.push(temp);
            if(temp.leftChild != null)
                stack1.push(temp.leftChild);
            if(temp.rightChild != null)
                stack1.push(temp.rightChild);
        }
        while(!stack2.isEmpty()){
            temp = stack2.pop();
            System.out.print(temp.data +" ");
        }
    }


    public static void main(String[] args) {
        //LinkedList是一个双向链表,有指向下一节点的指针next和指向前一节点的指针prev
        //泛型,LinkedList中只能存放整形变量
        //Integer是int的包装类,装箱:将基本数据类型(如int)转换为包装类(如Integer),拆箱:将包装类转换为基本数据类型,有自动装箱/拆箱和手动装箱/拆箱之分。
        //Arrays类提供了操纵数组的方法,如Arrays.toString(),Arrays.sort(Object[] array),Arrays.asList(整型数组),该方法将整型数组转化为List
        LinkedList<Integer> inputList = new LinkedList<Integer>(Arrays.asList(new Integer[]
                {3,2,9,null,null,10,null,null,8,null,4}));//前序遍历序列,包括叶子的null节点
        TreeNode treeNode = createBinaryTree(inputList);
        System.out.println("前序遍历递归方式:");
        preOrderTraveral(treeNode);
        System.out.println();
        System.out.println("中序遍历递归方式:");
        inOrderTraveral(treeNode);
        System.out.println();
        System.out.println("后序遍历递归方式:");
        postOrderTraveral(treeNode);
        System.out.println();
        System.out.println("前序遍历非递归方式:");
        preOrder(treeNode);
        System.out.println();
        System.out.println("中序遍历非递归方式:");
        inOrder(treeNode);
        System.out.println();
        System.out.println("后序遍历非递归方式:");
        postOrder(treeNode);
        System.out.println();
        System.out.println("层序遍历:");
        levelOrder(treeNode);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值