java操作二叉树:构建二叉树;前序、中序、后续、层次遍历

概念

二叉树

在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树二叉堆
二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有 2^{i-1} 个结点;深度为k的二叉树至多有 2^k-1 个结点。

对于树的基本概念上理解,对于才接触数据结构的人来说,树的高度和深度是一个容易混淆的知识点,现解释如下:

高度
对于高度的理解,我们不管他数据结构什么什么知识,就拿楼房来说,假如一个人提问:楼房的高度有好高?我们会下意识的从底层开始往上数,假如楼有6层,则我们会说,这个楼有6层楼那么高,则提问者就会大概知道楼有多高了。所以高度就是以从下往上对比,这是我们的习惯。而在树中,树的高度也是从下往上数,如图所示

K节点在树的底层,是一个叶子节点,则一般定义为K的高度在最低为1,以此类推,O的高度也是为1,P的节点也是为1。M节点是叶子节点O的父节点,从下往上数,M节点高度为2。那么G节点的高度是多少呢?从G-L的高度为2,从G-M-O节点高度为3,到底G节点高度为多少呢,正确答案是3,请看定义:

高度的定义为:从结点x向下到某个叶结点最长简单路径中边的条数

深度
理解了高度,则深度的理解就很容易了,深度是从根节点往下,列如上图中:B的深度为2。

总结
对于整棵树来说,最深的叶结点的深度就是树的深度;树根的高度就是树的高度。这样树的高度和深度是相等的。

对于树中相同深度的每个结点来说,它们的高度不一定相同,这取决于每个结点下面的叶结点的深度。

满二叉树: 一棵深度为k,且有2^k-1个节点称之为满二叉树。也就是除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
完全二叉树: 深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中,序号为1至n的节点对应时,称之为完全二叉树
平衡二叉树: 平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。[
二叉排序树:
二叉排序树又叫二叉查找树或者二叉搜索树,它首先是一个二叉树,而且必须满足下面的条件:

  1. 若左子树不空,则左子树上所有结点的值均小于它的根节点的值;
  2. 若右子树不空,则右子树上所有结点的值均大于它的根结点的值
  3. 左、右子树也分别为二叉排序树
  4. 没有键值相等的节点(?可能是因为不好处理键值相等的节点到底是左节点还是右节点吧)

这里写图片描述

上图中:a: 空二叉树;b:只有一个根结点的二叉树;c:只有左子树;d:只有右子树;e:完全二叉树

二叉树性质
(1) 在非空二叉树中,第i层的结点总数不超过 , i>=1;
(2) 深度为h的二叉树最多有 个结点(h>=1),最少有h个结点;
(3) 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
(4) 具有n个结点的完全二叉树的深度为
(5)有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:
若I为结点编号则 如果I>1,则其父结点的编号为I/2;
如果2I<=N,则其左儿子(即左子树的根结点)的编号为2I;若2I>N,则无左儿子;
如果2
I+1<=N,则其右儿子的结点编号为2I+1;若2I+1>N,则无右儿子。
(6)给定N个节点,能构成h(N)种不同的二叉树。
h(N)为卡特兰数的第N项。h(n)=C(2*n,n)/(n+1)。
(7)设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和J=I+2i[4]

二叉堆

二叉堆 是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆 :父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。
这里写图片描述

堆的存储

一般用数组来表示堆,若根结点存在序号0处, i结点的父结点下标就为(i-1)/2。i结点的左右子结点下标分别为2i+1和2i+2。如第0个结点左右子结点下标分别为1和2。
这里写图片描述

堆排序

请参考博主的另一篇文章 java排序算法汇总

接下来我们将对简单的二叉树进行操作。

java构建二叉树以及前序遍历、中序遍历、后序遍历

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Stack;

/**
 * Created by liubenlong on 2017/2/10.
 * 二叉树
 */
public class BinaryTree {

    /**
     * root节点
     */
    private Node root = null;

    /**
     * 二叉树节点数据结构
     */
    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    private static class Node{
        private int data;
        private Node leftNode;
        private Node rightNode;
    }

    /**
     * 构建二叉排序树
     * @param data
     */
    public void insert(int data){
        root = insert(root, data);
    }

    public Node insert(Node node, int data){
        if(node == null) node = new Node(data, null, null);
        else {
            if(data > node.getData()) node.setRightNode(insert(node.getRightNode(), data)) ;
            else node.setLeftNode(insert(node.getLeftNode(), data));
        }
        return node;
    }

    /**
     * 先序遍历-递归
     */
    public static void preOrderTraverse(Node node){
        if(node != null){
            System.out.print(node.getData() + ", ");
            preOrderTraverse(node.getLeftNode());
            preOrderTraverse(node.getRightNode());
        }
    }

    /**
     * 中序遍历-递归
     * @param node
     */
    public static void inOrderTraverse(Node node){
        if(node != null){
            inOrderTraverse(node.getLeftNode());
            System.out.print(node.getData() + ", ");
            inOrderTraverse(node.getRightNode());
        }
    }
    /**
     * 后续遍历-递归
     */
    public static void postOrderTraverse(Node node){
        if(node != null){
            postOrderTraverse(node.getLeftNode());
            postOrderTraverse(node.getRightNode());
            System.out.print(node.getData() + ", ");
        }
    }

    /**
     * 先序遍历-非递归
     * 思路和递归一样,只是这里将递归使用的栈显示的记录而已
     */
    public static void preOrderTraverse1(Node node){
        Stack<Node> stack = new Stack<>();
        if(node != null) stack.add(node);

        while(!stack.empty()){
            Node nodeTemp = stack.pop();
            System.out.print(nodeTemp.getData() + ", ");
            if(nodeTemp.getRightNode()!= null) stack.add(nodeTemp.getRightNode());
            if(nodeTemp.getLeftNode()!=null) stack.add(nodeTemp.getLeftNode());
        }

    }

    /**
     * 中序遍历-非递归
     * @param node
     */
    public static void inOrderTraverse1(Node node){
        Stack<Node> stack = new Stack<>();
        if(node != null) stack.add(node);

        while(!stack.empty()){
            while(node.getLeftNode()!= null) {
                stack.add(node.getLeftNode());
                node = node.getLeftNode();
            }

            Node nodeTemp = stack.pop();
            System.out.print(nodeTemp.getData() + ", ");

            if(nodeTemp.getRightNode() != null) {
                stack.add(nodeTemp.getRightNode());
                node = nodeTemp.getRightNode();
            }
        }
    }
    
    //层次遍历。比较简答,一个队列搞定
    public static void LevelOrder(Node node){
        Queue<Node> queue = new ArrayDeque<>();
        if (node != null)  queue.add(node);

        while(!queue.isEmpty()){
            Node poll = queue.remove();
            System.out.print(poll.getData() + ", ");
            if(poll.getLeftNode()!=null)queue.add(poll.getLeftNode());
            if(poll.getRightNode()!=null)queue.add(poll.getRightNode());
        }
    }
    
    /**    
     * 后续遍历-非递归
     * 这个麻烦一点
     */
    public static void postOrderTraverse1(Node node){
        Node lastPrintNode = node;
        Stack<Node> stack = new Stack<>();
        while (node != null) {
            // 左子树入栈
            for (; node.getLeftNode() != null; node = node.getLeftNode())
                stack.push(node);
            // 当前节点无右子或右子已经输出
            while (node != null && (node.getRightNode() == null || node.getRightNode() == lastPrintNode)) {
                System.out.print(node.getData() + ", ");
                lastPrintNode = node;// 记录上一个已输出节点
                if (stack.empty())
                    return;
                node = stack.pop();
            }
            // 处理右子
            stack.push(node);
            node = node.getRightNode();
        }        
    }


    //计算深度--递归,依次计算其左右子树的深度,选其大者
    public static int deep(Node node){
        if(node == null) return 0;

        //不为空则初始化深度为1
        int leftDeep = 1, rightDeep = 1;
        if(node.getLeftNode() != null) leftDeep += deep(node.getLeftNode());
        if(node.getRightNode() != null) rightDeep += deep(node.getRightNode());

        return leftDeep > rightDeep ? leftDeep : rightDeep;
    }
	/**
     * 计算深度(第二种写法)
     */
    public static int deep1(Node node) {
        if (null == node) return 0;
        
        int leftDeep = deep(node.getLeftNode());
        int rightDeep = deep(node.getRightNode());
        return 1 + (leftDeep > rightDeep ? leftDeep : rightDeep);
    }

    public static void main(String[] args){
        /**
         * 构建二叉树
         */
        BinaryTree tree = new BinaryTree();
        tree.insert(3);
        tree.insert(6);
        tree.insert(1);
        tree.insert(2);
        tree.insert(9);
        tree.insert(8);
        tree.insert(10);



        tree.preOrderTraverse(tree.root);
        System.out.println();
        tree.inOrderTraverse(tree.root);
        System.out.println();
        tree.postOrderTraverse(tree.root);

        System.out.println();
        System.out.println();
        preOrderTraverse1(tree.root);
        System.out.println();
        inOrderTraverse1(tree.root);
        System.out.println();
        postOrderTraverse1(tree.root);
        
        //层次遍历
        System.out.println();
        LevelOrder(tree.root);

        System.out.println();
        int deep = deep(tree.root);
        System.out.println("该二叉树的深度为:"+deep);
        deep = deep1(tree.root);
        System.out.println("【1】该二叉树的深度为:"+deep);
    }
}

输出结果

3, 1, 2, 6, 9, 8, 10, 
1, 2, 3, 6, 8, 9, 10, 
2, 1, 8, 10, 9, 6, 3, 

3, 1, 2, 6, 9, 8, 10, 
1, 2, 3, 6, 8, 9, 10, 
2, 1, 8, 10, 9, 6, 3, 
3, 1, 6, 2, 9, 8, 10, 
该二叉树的深度为:4
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐崇拜234

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值