二叉树就是这么简单

前言

只有光头才能变强。
文本已收录至个人GitHub仓库,欢迎Star: https://github.com/ZhongFuCheng3y/3y

1、二叉树就是这么简单

本文撇开一些很是苦涩、难以理解的概念来说讲二叉树,仅入门观看(或复习)....java

首先,咱们来说讲什么是树:git

  • 树是一种非线性的数据结构,相对于线性的数据结构(链表、数组)而言,树的平均运行时间更短(每每与树相关的排序时间复杂度都不会高)

在现实生活中,咱们通常的树长这个样子的:github

可是在编程的世界中,咱们通常把树“倒”过来看,这样容易咱们分析:编程

通常的树是有不少不少个分支的,分支下又有不少不少个分支,若是在程序中研究这个会很是麻烦。由于原本树就是非线性的,而咱们计算机的内存是线性存储的,太过复杂的话咱们没法设计出来的。数组

所以,咱们先来研究简单又常常用的---> 二叉树数据结构

1.1树的一些概念

我就拿上面的图来进行画来说解了:测试

二叉树的意思就是说:每一个节点不能多于有两个儿子,上面的图就是一颗二叉树。this

  • 一棵树至少会有一个节点(根节点)
  • 树由节点组成,每一个节点的数据结构是这样的:
  • 所以,咱们定义树的时候每每是->定义节点->节点链接起来就成了树,而节点的定义就是:一个数据、两个指针(若是有节点就指向节点、没有节点就指向null)

1.2静态建立二叉树

上面说了,树是由若干个节点组成,节点链接起来就成了树,而节点由一个数据、两个指针组成spa

  • 所以,建立树实际上就是建立节点,而后链接节点

首先,使用Java类定义节点:设计

public class TreeNode {

    // 左节点(儿子)
    private TreeNode lefTreeNode;
    
    // 右节点(儿子)
    private TreeNode rightNode;
    
    // 数据
    private int value;
    

}

下面咱们就拿这个二叉树为例来构建吧:

为了方便构建,我就给了它一个带参数的构造方法和set、get方法了:

public TreeNode(int value) {

        this.value = value;
    }

那么咱们如今就建立了5个节点:

public static void main(String[] args) {

        //根节点-->10
        TreeNode treeNode1 = new TreeNode(10);

        //左孩子-->9
        TreeNode treeNode2 = new TreeNode(9);

        //右孩子-->20
        TreeNode treeNode3 = new TreeNode(20);
        
        //20的左孩子-->15
        TreeNode treeNode4 = new TreeNode(15);
        
        //20的右孩子-->35
        TreeNode treeNode5 = new TreeNode(35)        
      
    }

它们目前的状态是这样子的:

因而下面咱们去把它连起来:

//根节点的左右孩子
    treeNode1.setLefTreeNode(treeNode2);
    treeNode1.setRightNode(treeNode3);

    //20节点的左右孩子
    treeNode3.setLefTreeNode(treeNode4);
    treeNode3.setRightNode(treeNode5);

链接完以后,那么咱们的树就建立完成了。

1.3遍历二叉树

上面说咱们的树建立完成了,那怎么证实呢??咱们若是能够像数组同样遍历它(看它的数据),那就说明它建立完成了

值得说明的是:二叉树遍历有三种方式

  • 先序遍历

    • 先访问根节点,而后访问左节点,最后访问右节点(根->左->右)
  • 中序遍历

    • 先访问左节点,而后访问根节点,最后访问右节点(左->根->右)
  • 后序遍历

    • 先访问左节点,而后访问右节点,最后访问根节点(左->右->根)

以上面的二叉树为例:

  • 若是是先序遍历10->9->20->15->35
  • 若是是中序遍历9->10->15->20->35

    • 可能须要解释地方:访问完10节点事后,去找的是20节点,但20下还有子节点,所以访问的是20的左儿子15节点。因为15节点没有儿子了。因此就返回20节点,访问20节点。最后访问35节点
  • 若是是后序遍历9->15->35->20->10

    • 可能须要解释地方:先访问9节点,随后应该访问的是20节点,但20下还有子节点,所以访问的是20的左儿子15节点。因为15节点没有儿子了。因此就去访问35节点,因为35节点也没有儿子了,因此返回20节点,最终返回10节点

一句话总结:先序(根->左->右),中序(左->根->右),后序(左->右->根)。若是访问有孩子的节点,先处理孩子的,随后返回

不管先中后遍历,每一个节点的遍历若是访问有孩子的节点,先处理孩子的(逻辑是同样的)

  • 所以咱们很容易想到递归
  • 递归的出口就是:当没有子节点了,就返回

所以,咱们能够写出这样的先序遍历代码

/**
     * 先序遍历
     * @param rootTreeNode  根节点
     */
    public static void preTraverseBTree(TreeNode rootTreeNode) {

        if (rootTreeNode != null) {

            //访问根节点
            System.out.println(rootTreeNode.getValue());

            //访问左节点
            preTraverseBTree(rootTreeNode.getLefTreeNode());

            //访问右节点
            preTraverseBTree(rootTreeNode.getRightNode());
        }
    }

结果跟咱们刚才说的是同样的:

咱们再用中序遍历调用一遍吧:

/**
     * 中序遍历
     * @param rootTreeNode  根节点
     */
    public static void inTraverseBTree(TreeNode rootTreeNode) {

        if (rootTreeNode != null) {

            //访问左节点
            inTraverseBTree(rootTreeNode.getLefTreeNode());

            //访问根节点
            System.out.println(rootTreeNode.getValue());

            //访问右节点
            inTraverseBTree(rootTreeNode.getRightNode());
        }
    }

结果跟咱们刚才说的是同样的:

有意思的是:经过先序和中序或者中序和后序咱们能够还原出原始的二叉树,可是经过先序和后序是没法还原出原始的二叉树的

  • 也就是说:经过中序和先序或者中序和后序咱们就能够肯定一颗二叉树了

2、动态建立二叉树

上面咱们是手动建立二叉树的,通常地:都是给出一个数组给你,让你将数组变成一个二叉树,此时就须要咱们动态建立二叉树了。

二叉树中还有一种特殊的二叉树:二叉查找树(binary search tree)

  • 定义:当前根节点的左边所有比根节点小,当前根节点的右边所有比根节点大

    • 明眼人能够看出,这对咱们来找一个数是很是方便快捷的

每每咱们动态建立二叉树都是建立二叉查找树

2.1动态建立二叉树体验

假设咱们有一个数组:int[] arrays = {3, 2, 1, 4, 5};

那么建立二叉树的步骤是这样的:

  • 首先将3做为根节点

  • 随后2进来了,咱们跟3作比较,比3小,那么放在3的左边

  • 随后1进来了,咱们跟3作比较,比3小,那么放在3的左边,此时3的左边有2了,所以跟2比,比2小,放在2的左边

  • 随后4进来了,咱们跟3作比较,比3大,那么放在3的右边

  • 随后5进来了,咱们跟3作比较,比3大,那么放在3的右边,此时3的右边有4了,所以跟4比,比4大,放在4的右边

那么咱们的二叉查找树就创建成功了,不管任何一颗子树,左边都比根要小,右边比根要大

2.2代码实现

咱们的代码实现也很简单,若是比当前根节点要小,那么放到当前根节点左边,若是比当前根节点要大,那么放到当前根节点右边。

由于是动态建立的,所以咱们得用一个类来表示根节点

public class TreeRoot {

    private TreeNode treeRoot;

    public TreeNode getTreeRoot() {
        return treeRoot;
    }

    public void setTreeRoot(TreeNode treeRoot) {
        this.treeRoot = treeRoot;
    }
}

比较与根谁大,大的往右边,小的往左边:

/**
     * 动态建立二叉查找树
     *
     * @param treeRoot 根节点
     * @param value    节点的值
     */
    public static void createTree(TreeRoot treeRoot, int value) {


        //若是树根为空(第一次访问),将第一个值做为根节点
        if (treeRoot.getTreeRoot() == null) {
            TreeNode treeNode = new TreeNode(value);
            treeRoot.setTreeRoot(treeNode);

        } else  {

            //当前树根
            TreeNode tempRoot = treeRoot.getTreeRoot();

            while (tempRoot != null) {
                //当前值大于根值,往右边走
                if (value > tempRoot.getValue()) {

                    //右边没有树根,那就直接插入
                    if (tempRoot.getRightNode() == null) {
                        tempRoot.setRightNode(new TreeNode(value));
                        return ;
                    } else {
                        //若是右边有树根,到右边的树根去
                        tempRoot = tempRoot.getRightNode();
                    }
                } else {
                    //左没有树根,那就直接插入
                    if (tempRoot.getLefTreeNode() == null) {
                        tempRoot.setLefTreeNode(new TreeNode(value));

                        return;
                    } else {
                        //若是左有树根,到左边的树根去
                        tempRoot = tempRoot.getLefTreeNode();
                    }
                }
            }
        }
    }

测试代码:

int[] arrays = {2, 3, 1, 4, 5};

    //动态建立树

    TreeRoot root = new TreeRoot();
    for (int value : arrays) {
        createTree(root, value);
    }


    //中序遍历树
    inTraverseBTree(root.getTreeRoot());
    System.out.println("---------------公众号:Java3y");
    
    //先序遍历树
    preTraverseBTree(root.getTreeRoot());
    System.out.println("---------------公众号:Java3y");

3、查询二叉查找树相关

3.1查询树的深度

查询树的深度咱们能够这样想:左边的子树和右边的字数比,谁大就返回谁,那么再接上根节点+1就能够了

public static int getHeight(TreeNode treeNode) {

        if (treeNode == null) {
            return 0;
        } else {

            //左边的子树深度
            int left = getHeight(treeNode.getLefTreeNode());

            //右边的子树深度
            int right = getHeight(treeNode.getRightNode());


            int max = left;

            if (right > max) {
                max = right;
            }
            return max + 1;
        }
    }

3.1查询树的最大值

从上面先序遍历二叉查找树的时候,细心的同窗可能会发现:中序遍历二叉查找树获得的结果是排好顺序的~

那么,若是咱们的二叉树不是二叉查找树,咱们要怎么查询他的最大值呢

能够这样:

  • 左边找最大值->递归
  • 右边找最大值->递归
/**
     * 找出树的最大值
     *
     * @param rootTreeNode
     */
    public static int  getMax(TreeNode rootTreeNode) {

        if (rootTreeNode == null) {
            return -1;
        } else {
            //找出左边的最大值
            int left = getMax(rootTreeNode.getLefTreeNode());

            //找出右边的最大值
            int right = getMax(rootTreeNode.getRightNode());

            //与当前根节点比较
            int currentRootValue = rootTreeNode.getValue();

            //假设左边的最大
            int max = left;


            if (right > max) {
                max = right;
            }
            if (currentRootValue > max) {
                max = currentRootValue;
            }

            return max ;


        }
    }

4、最后

不管是在遍历树、查找深度、查找最大值都用到了递归,递归在非线性的数据结构中是用得很是多的...

树的应用也很是普遍,此篇简单地说明了树的数据结构,高级的东西我也没弄懂,可能之后用到的时候会继续深刻...

乐于输出 干货的Java技术公众号:Java3y。公众号内有200多篇 原创技术文章、海量视频资源、精美脑图,不妨来 关注一下!

帅的人都关注了

相关文章
相关标签/搜索