博主强烈建议跳过分割线前面的部分,直接看下文更新的那些即可。


最近在学习二叉树的相关知识,一开始真的是毫无头绪。本来学的是C++二叉树,但苦于编译器老是出故障,于是就转用Java来实现二叉树的操作。但是二者原理是一致的,而且实现的方式也是大同小异!

下面就让我们来看看代码吧。

1、首先我们需要创建一个二叉树的节点类,便于我们对树的操作,当然了,你也可以在二叉树类的内部将节点类声明为内部类,但是这样会降低操作的灵活性。我才用的是单独创建一个BinaryTreeNode类,代码如下:

package MyBinaryTree;

public class BinaryTreeNode<T> {

    T data;
    BinaryTreeNode<T> leftChild;
    BinaryTreeNode<T> rightChild;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    BinaryTreeNode() {
        this.data = null;
        this.leftChild = null;
        this.rightChild = null;
    }

    BinaryTreeNode(T data) {
        this.data = data;
        this.leftChild = null;
        this.rightChild = null;
    }

    public BinaryTreeNode(T data, BinaryTreeNode<T> leftChild,
            BinaryTreeNode<T> rightChild) {
        super();
        this.data = data;
        this.leftChild = leftChild;
        this.rightChild = rightChild;
    }

    public BinaryTreeNode<T> getLeftChild() {
        return leftChild;
    }

    public void setLeftChild(BinaryTreeNode<T> leftChild) {
        this.leftChild = leftChild;
    }

    public BinaryTreeNode<T> getRightChild() {
        return rightChild;
    }

    public void setRightChild(BinaryTreeNode<T> rightChild) {
        this.rightChild = rightChild;
    }

    public boolean isLeaf() {
        if (this.leftChild == null && this.rightChild == null) {
            return true;
        }
        return false;
    }

}
//我才用的是泛型定义,在C++中我们可以使用模板来实现相同的处理

2、有了节点类,下面就是二叉树类了,注释什么的在代码中已经非常详细了:

package MyBinaryTree;

import java.util.Queue;
import java.util.Stack;
import java.util.concurrent.LinkedBlockingQueue;

public class BinaryTree<T> {

    private BinaryTreeNode<T> root;

    public BinaryTree() {
        BinaryTreeNode<T> node = new BinaryTreeNode<T>();
        this.root = node;
    }

    public boolean isEmpty() {
        if (root == null) {
            return true;
        }
        return false;
    }

    public BinaryTreeNode<T> getRoot() {
        return this.root;
    }

    public void CreateTree(BinaryTreeNode<T> node, T data) {
        if (root == null) {
            root = new BinaryTreeNode<T>();
        } else {
            if (Math.random() > 0.5) {                   //采用随机方式创建二叉树
                if (node.leftChild == null) {
                    node.leftChild = new BinaryTreeNode<T>(data);
                } else {
                    CreateTree(node.leftChild, data);
                }
            } else {
                if (node.rightChild == null) {
                    node.rightChild = new BinaryTreeNode<T>(data);
                } else {
                    CreateTree(node.rightChild, data);
                }
            }
        }
    }

    /*
     * 访问当前节点
     */
    public void Visit(BinaryTreeNode<T> current) {
        if(current!=null&&current.getData()!=null){
            System.out.println(current.getData());
        }else{
            System.out.println("null");
        }
    }

    /*
     * 广度优先遍历二叉树
     */
    public void levelOrder(BinaryTreeNode<T> root) {
        Queue<BinaryTreeNode<T>> queue = new LinkedBlockingQueue<BinaryTreeNode<T>>();
        BinaryTreeNode<T> pointer = root;
        /*
         * 当前节点不为空时,放入队首
         */
        if (pointer != null) {
            queue.add(pointer);
        }
        /*
         * 队列不为空时,先访问中间节点,访问完成后弹出队首节点;然后是左节点,再是右节点;
         */
        while (!queue.isEmpty()) {
            pointer = queue.peek();
            Visit(pointer);
            queue.remove();
            if (pointer.leftChild != null) {
                queue.add(pointer.leftChild);
            }
            if (pointer.rightChild != null) {
                queue.add(pointer.rightChild);
            }
        }
    }

    /*
     * 递归方式的前序遍历
     */
    public void preOrder(BinaryTreeNode<T> root) {
            Visit(root);
            preOrder(root.leftChild);
            preOrder(root.rightChild);
    }

    /*
     * 非递归方式实现的前序遍历
     */
    public void NPreOrder(BinaryTreeNode<T> root){
        Queue<BinaryTreeNode<T>> queue=new LinkedBlockingQueue<BinaryTreeNode<T>>();
        BinaryTreeNode<T> pointer=root;
        /*
         * 当前节点不为空,就一直放入队尾;当前节点为空时,访问队首元素,然后访问做孩子节点;然后弹出,再对新的队首元素进行判断
         */
        while(!queue.isEmpty()||pointer!=null){
            if(pointer!=null){
                Visit(pointer);
                if(pointer.rightChild!=null){
                    queue.add(pointer.rightChild);
                }
                pointer=pointer.leftChild;
            }else{
                pointer=queue.peek();
                queue.remove();
            }
        }
    }

    /*
     * 采用递归方式实现的中序遍历操作
     */
    public void inOrder(BinaryTreeNode<T> root){
        inOrder(root.leftChild);
        Visit(root);
        inOrder(root.rightChild);
    }

    /*
     * 非递归方式实现的中序遍历
     */
    public void NInOrder(BinaryTreeNode<T> root){
        Stack<BinaryTreeNode<T>> stack=new Stack<BinaryTreeNode<T>>();
        BinaryTreeNode<T> pointer=root;
        /*
         * 当前节点不为空,就一直进栈;当前节点为空时,访问栈顶元素,然后再访问右孩子节点
         */
        while(!stack.isEmpty()||pointer!=null){
            if(pointer!=null){
                stack.push(pointer);
                pointer=pointer.leftChild;
            }else{
                pointer=stack.peek();
                Visit(pointer);
                pointer=pointer.rightChild;
                stack.pop();
            }
        }
    }

    /*
     * 递归方式实现的后序遍历二叉树
     */
    public void postOrder(BinaryTreeNode<T> root){
        postOrder(root.leftChild);
        postOrder(root.rightChild);
        Visit(root);
    }

    /*
     * 非递归方式实现的后序遍历二叉树
     */
    public void NPostOrder(BinaryTreeNode<T> root){
        Stack<BinaryTreeNode<T>> stack=new Stack<BinaryTreeNode<T>>();//初始化栈,用于保存带访问的节点
        BinaryTreeNode<T> pointer=root;                               //保存根节点
        BinaryTreeNode<T> preNode=root;                               //保存前一个被访问的节点
        while(!stack.isEmpty()||pointer!=null){
            //若当前节点不空,就一直进栈,然后继续向左走
            while(pointer.leftChild!=null){
                stack.push(pointer);
                pointer=pointer.leftChild;
            }
            /*
             * 当前节点为空时,分两种情况:
             * 1、当前节点移动到栈顶处,然后访问栈顶元素的右节点
             * 2、当前节点移动到栈顶,但是栈顶元素没有右节点,这就需要弹出栈顶元素,再对此元素访问;
             * 然后再对新的栈顶元素进行判断即可
             */
            while(pointer!=null&&(pointer.rightChild==null)||(pointer.rightChild==preNode)){
                Visit(pointer);
                preNode=pointer;
                if(stack.isEmpty()){
                    return;
                }
                pointer=stack.peek();
                stack.pop();
            }
                stack.push(pointer);
                pointer=pointer.rightChild;

        }
    }
}

3、然后是我的测试类,下面请看代码:

    public static void main(String[] args) {
        BinaryTree<Integer> tree = new BinaryTree<Integer>();
        for (int i = 1; i < 10; i++) {
            tree.CreateTree(tree.root, i);
        }
        System.out.println("-----------下面是广度优先遍历二叉树--------------");
        tree.levelOrder(tree.root);
        System.out.println("-----------下面是非递归的前序遍历方式-------------");
        tree.NPreOrder(tree.root);
        System.out.println("-----------下面是非递归的中序遍历方式-------------");
        tree.NInOrder(tree.root);
        System.out.println("-----------下面是非递归的后序遍历方式-------------");
        tree.NPostOrder(tree.root);
    }

4、接下来是测试的结果:

-----------下面是广度优先遍历二叉树--------------
null
1
2
6
4
3
8
7
5
9
-----------下面是非递归的前序遍历方式-------------
null
1
6
2
3
4
5
7
8
9
-----------下面是非递归的中序遍历方式-------------
6
7
1
5
4
null
3
9
2
8
-----------下面是非递归的后序遍历方式-------------
7
6
5
4
1
9
3
8
2
null

5、不足之处:

也许是测试的时候方式不对,因为使用递归方式对二叉树进行遍历的时候会报出NullPointerException的空指针错误。如果你知道原因在哪?不妨写下你的评论。也好让我加以改正。

6、总结:

在学习的过程中我意识到了一点,希望与君共勉!那就是埋头敲代码是解决不了问题的。重要的是思路。没有思路,一味的测试也是不可能成功的。在敲代码之前,我们一定要搞懂我们要做什么,怎么做,这样才会事半功倍。希望能和大家共同学习,一起进步!

—————————时间的分割线——————————————————————–

2016年9月12日19:01:38

看到大二刚开始学数据结构的时候,写的这篇文章,水平真的是不忍直视啊。不过话又说回来了,编程的提高不就是这样一点一点积聚来的嘛。下面来写点比较容易理解的思路清晰的二叉树遍历相关的操作。

/**
 * @Date 2016年9月12日
 *
 * @author 郭  璞
 *
 */
package tree;

import java.util.Stack;

/**
 * @author 郭 璞 <br>
 *         二叉树的先序,中序,以及后序,递归以及非递归的实现
 *
 */
public class FullScan {

    public static void main(String[] args) {
        Node head = createTree();
        // recurseFront(head);
        // recurseMid(head);
        recurseEnd(head);
        // front(head);
        // mid(head);
        endWith2Stack(head);
        endWithOneStack(head);
    }

    /**
     * 非递归实现的二叉树后序遍历<br>
     * 借助于一个栈进行实现
     *
     * @param head
     */
    public static void endWithOneStack(Node head) {
        System.out.println();
        if (head == null) {
            return;
        } else {
            Stack<Node> stack = new Stack<Node>();
            stack.push(head);
            // 该节点代表已经打印过的节点,待会会及时的进行更新
            Node printedNode = null;
            while (!stack.isEmpty()) {
                // 获取 栈顶的元素的值,而不是pop掉栈顶的值
                head = stack.peek();
                // 如果当前栈顶元素的左节点不为空,左右节点均未被打印过,说明该节点是全新的,所以压入栈中
                if (head.getLeft() != null && printedNode != head.getLeft() && printedNode != head.getRight()) {
                    stack.push(head.getLeft());
                } else if (head.getRight() != null && printedNode != head.getRight()) {
                    // 第一层不满足,则说明该节点的左子树已经被打印过了。如果栈顶元素的右节点未被打印过,则将右节点压入栈中
                    stack.push(head.getRight());
                } else {
                    // 上面两种情况均不满足的时候则说明左右子树均被打印过,此时只需要弹出栈顶元素,打印该值即可
                    System.out.println("当前值为:" + stack.pop().getValue());
                    // 记得实时的更新打印过的节点的值
                    printedNode = head;
                }
            }
        }
    }

    /**
     * 非递归实现的二叉树的后序遍历<br>
     * 借助于两个栈来实现
     *
     * @param head
     */
    public static void endWith2Stack(Node head) {
        System.out.println();
        if (head == null) {
            return;
        } else {
            Stack<Node> stack1 = new Stack<Node>();
            Stack<Node> stack2 = new Stack<Node>();

            stack1.push(head);
            // 对每一个头结点进行判断,先将头结点放入栈2中,然后依次将该节点的子元素放入栈1.顺序为left-->right。便是因为后序遍历为“左右根”
            while (!stack1.isEmpty()) {
                head = stack1.pop();
                stack2.push(head);
                if (head.getLeft() != null) {
                    stack1.push(head.getLeft());
                }

                if (head.getRight() != null) {
                    stack1.push(head.getRight());
                }
            }

            // 直接遍历输出栈2,即可实现后序遍历的节点值的输出
            while (!stack2.isEmpty()) {
                System.out.println("当前节点的值:" + stack2.pop().getValue());
            }
        }
    }

    /**
     * 非递归实现的二叉树的中序遍历
     *
     * @param head
     */
    public static void mid(Node head) {
        System.out.println();
        if (head == null) {
            return;
        } else {
            Stack<Node> nodes = new Stack<Node>();

            // 使用或的方式是因为 第一次的时候战中元素为空,head的非null特性可以保证程序可以执行下去
            while (!nodes.isEmpty() || head != null) {
                // 当前节点元素值不为空,则放入栈中,否则先打印出当前节点的值,然后将头结点变为当前节点的右子节点。
                if (head != null) {
                    nodes.push(head);
                    head = head.getLeft();
                } else {
                    Node temp = nodes.pop();
                    System.out.println("当前节点的值:" + temp.getValue());
                    head = temp.getRight();
                }
            }

        }
    }

    /**
     * 非递归实现的二叉树的先序遍历
     *
     * @param head
     */
    public static void front(Node head) {
        System.out.println();

        // 如果头结点为空,则没有遍历的必要性,直接返回即可
        if (head == null) {
            return;
        } else {
            // 初始化用于存放节点顺序的栈结构
            Stack<Node> nodes = new Stack<Node>();
            // 先把head节点放入栈中,便于接下来的循环放入节点操作
            nodes.add(head);

            while (!nodes.isEmpty()) {
                // 取出栈顶元素,判断其是否有子节点
                Node temp = nodes.pop();

                System.out.println("当前节点的值:" + temp.getValue());
                // 先放入右边子节点的原因是先序遍历的话输出的时候左节点优先于右节点输出,而栈的特性决定了要先放入右边的节点
                if (temp.getRight() != null) {
                    nodes.push(temp.getRight());
                }
                if (temp.getLeft() != null) {
                    nodes.push(temp.getLeft());
                }
            }
        }
    }

    /**
     * 递归实现的先序遍历
     *
     * @param head
     */
    public static void recurseFront(Node head) {
        System.out.println();

        if (head == null) {
            return;
        }
        System.out.println("当前节点值:" + head.getValue());
        recurseFront(head.left);
        recurseFront(head.right);
    }

    /**
     * 递归实现的中序遍历
     *
     * @param head
     */
    public static void recurseMid(Node head) {
        System.out.println();
        if (head == null)
            return;
        recurseMid(head.getLeft());
        System.out.println("当前节点的值:" + head.getValue());
        recurseMid(head.getRight());
    }

    /**
     * 递归实现的后序遍历递归实现
     *
     * @param head
     */
    public static void recurseEnd(Node head) {
        System.out.println();
        if (head == null)
            return;
        recurseEnd(head.getLeft());
        recurseEnd(head.getRight());
        System.out.println("当前节点的值为:" + head.getValue());
    }

    public static Node createTree() {
        // 初始化节点
        Node head = new Node(1);
        Node headLeft = new Node(2);
        Node headRight = new Node(3);
        Node headLeftLeft = new Node(4);
        Node headLeftRigth = new Node(5);
        Node headRightLeft = new Node(6);
        // 为head节点 赋予左右值
        head.setLeft(headLeft);
        head.setRight(headRight);

        headLeft.setLeft(headLeftLeft);
        headLeft.setRight(headLeftRigth);
        headRight.setLeft(headRightLeft);

        // 返回树根节点
        return head;
    }

}

class Node {
    public int value;
    public Node left;
    public Node right;

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public Node getLeft() {
        return left;
    }

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

    public Node getRight() {
        return right;
    }

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

    public Node() {
    }

    public Node(int value) {
        this.value = value;
    }
}

Java实现二叉树的创建和遍历操作(有更新)的更多相关文章

  1. c/c++二叉树的创建与遍历(非递归遍历左右中,破坏树结构)

    二叉树的创建与遍历(非递归遍历左右中,破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...

  2. 二叉树的创建、遍历(递归和非递归实现)、交换左右子数、求高度(c++实现)

    要求:以左右孩子表示法实现链式方式存储的二叉树(lson—rson),以菜单方式设计并完成功能任务:建立并存储树.输出前序遍历结果.输出中序遍历结果.输出后序遍历结果.交换左右子树.统计高度,其中对于 ...

  3. JS实现二叉树的创建和遍历

    1.先说二叉树的遍历,遍历方式: 前序遍历:先遍历根结点,然后左子树,再右子树 中序遍历:先遍历左子树,然后根结点,再右子树 后续遍历:先遍历左子树,然后右子树,再根结点   上代码:主要还是利用递归 ...

  4. Java实现二叉树的创建、递归/非递归遍历

    近期复习数据结构中的二叉树的相关问题,在这里整理一下 这里包含: 1.二叉树的先序创建 2.二叉树的递归先序遍历 3.二叉树的非递归先序遍历 4.二叉树的递归中序遍历 5.二叉树的非递归中序遍历 6. ...

  5. 二叉树的创建和遍历(C版和java版)

    以这颗树为例:#表示空节点前序遍历(根->左->右)为:ABD##E##C#F## 中序遍历(左->根->右)为:#D#B#E#A#C#F# 后序遍历(左->右-> ...

  6. JAVA实现File类中的遍历操作并输出内容

    package shb.java.testIo; import java.io.BufferedReader; import java.io.BufferedWriter; import java.i ...

  7. Java实现二叉树的构建与遍历

    转载:http://ocaicai.iteye.com/blog/1047397 目录: 1.把一个数组的值赋值给一颗二叉树 2.具体代码 1.树的构建方法 2.具体代码 package tree; ...

  8. 剑指offer十七姊妹篇之二叉树的创建、遍历、判断子二叉树

    1.二叉树节点类 public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public Tr ...

  9. 基于Java的二叉树的三种遍历方式的递归与非递归实现

    二叉树的遍历方式包括前序遍历.中序遍历和后序遍历,其实现方式包括递归实现和非递归实现. 前序遍历:根节点 | 左子树 | 右子树 中序遍历:左子树 | 根节点 | 右子树 后序遍历:左子树 | 右子树 ...

随机推荐

  1. debug的一些按钮意义

    F9 resume programe 恢复程序 Alt+F10 show execution point 显示执行断点 F8 Step Over 相当于eclipse的f6 跳到下一步 F7 Step ...

  2. Linux下安装java的jdk和配置环境变量

    每次感觉配这个超级简单 但是每次都要查下 记一下好了 Linux下安装jdk,步骤如下 1:下载jdk包:本章使用的为后缀为tar.gz的文件(不需要安装),如jdk-8u111-linux-x64. ...

  3. 监控undo空间和临时段的使用情况

    --1.监控undo空间情况 ),) free_space from dba_free_space where tablespace_name='UNDOTBS1' group by tablespa ...

  4. python学习之路web框架续

    中间件 django 中的中间件(middleware),在django中,中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法. 在django项 ...

  5. COCO 数据集的使用

    Windows 10 编译 Pycocotools 踩坑记 COCO数据库简介 微软发布的COCO数据库, 除了图片以外还提供物体检测, 分割(segmentation)和对图像的语义文本描述信息. ...

  6. python笔记九(迭代)

    一.迭代 通过for循环来遍历一个列表,我们称这种遍历的方式为迭代.只要是可迭代对象都可以进行迭代操作. 以下代码可以用来判断一个对象是否是可迭代的. 一类是集合数据类型,如list.tuple.di ...

  7. Spring Boot消息队列应用实践

    消息队列是大型复杂系统解耦利器.本文根据应用广泛的消息队列RabbitMQ,介绍Spring Boot应用程序中队列中间件的开发和应用. 一.RabbitMQ基础 1.RabbitMQ简介 Rabbi ...

  8. Template基础

    模板系统的介绍 你可能已经注意到我们在例子视图中返回文本的方式有点特别. 也就是说,HTML被直接硬编码在 Python代码之中. def current_datetime(request): now ...

  9. Android中典型的ROOT原理(5)

    ROOT的作用 Customization 用户的个人定制,如删除一些预安装,定制开机动画等. 特权操作 所有需要特权操作的基本都是要通过ROOT,这也是ROOT的初衷. ROOT的第一步:寻找漏洞并 ...

  10. 临时关闭Mac SIP系统完整性保护机制

    # 修正更新 [2016-12-27] 晚上给我笔记本安装的时候,使用user权限安装成功,mac最后是关闭sip才安装成功. $ pip install -r requirements.txt -- ...