Java实现二叉树的创建和遍历操作(有更新)

<font color='green' size='5'>博主强烈建议跳过分割线前面的部分,直接看下文更新的那些即可。


最近在学习二叉树的相关知识,一开始真的是毫无头绪。本来学的是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;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容

  • 树的概述 树是一种非常常用的数据结构,树与前面介绍的线性表,栈,队列等线性结构不同,树是一种非线性结构 1.树的定...
    Jack921阅读 4,454评论 1 31
  • 基于树实现的数据结构,具有两个核心特征: 逻辑结构:数据元素之间具有层次关系; 数据运算:操作方法具有Log级的平...
    yhthu阅读 4,272评论 1 5
  • 周末看天气不错,临时决定回趟老家,来一次说走就走的旅程。吃完早饭,碗都没来得及洗就出发了。 虽说离家不远,可回去的...
    李在在阅读 259评论 1 1
  • 每一个人进入大学的时候,都是新鲜的个体,未来的四年都是一张白纸。这张白纸的第一个点该怎么画,该画在那儿, 是一门学...
    懒小姐xx阅读 803评论 2 6