数据结构之二叉树的实现

一、树的简单了解

1.概念:
树是一种非线性数据结构,它是由 n (n>0) 个结点组成的一个具有层次结构的关系的集合。

形似一个倒置的树,根朝上,叶子朝下。

2.树的特点:
(1)树有一个特殊的节点,为根节点,根节点没有前驱结点。
(2)树是由递归方式定义的。
(3)树形结构中,子树之间不能有交集,否则不能称为树形结构。

二、树的结构概念

在这里插入图片描述

结点的度:一个结点含有子树的个数称为该结点的度; 如上图:A 的节点的度为 2。

树的度:一棵树中,所有结点度的最大值称为树的度; 如上图:树的度为 2 。

叶子结点或终端结点:度为0的结点称为叶结点; 如上图:D,H,F,G 为叶结点。

双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的
父结点, 如上图:A 为 B,C 的父节点。

孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点, 如上图:B,C 为 A 的孩子结点。

根结点:一棵树中,没有双亲结点的结点,如上图:A

结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推。

树的高度或深度:树中结点的最大层次,如上图:树的高度为4。

树的以下概念只需了解

非终端结点或分支结点:度不为0的结点; 如上图:A,B,C,E 为分支结点

兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点

堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:E,F 互为兄弟结点

结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先

子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙

三、二叉树

1.二叉树的概念:二叉树可以为空,或是由一个根节点加上一个左子树和一个右子树组成。

(1)二叉树不存在度大于2的节点。
(2)二叉树的结点元素是有顺序的,次序不能改变。

2.两种特殊的二叉树

(1)满二叉树:每层结点数都达到最大值,这棵树就是满二叉树。
在这里插入图片描述
(2)完全二叉树:对于深度为K的,有 n 个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从 0 至 n-1 的结点一 一对应时称之为完全二叉树。
在这里插入图片描述

四、代码实现二叉树

1.实现前准备

为了后续方法的实现更加方便,我们首先需要简单实现一下树的节点,并且简单创建一个树。
代码如下:

//树的每一个结点都是一个独立的部分,因此可以以内部类的形式实现。
    static class TreeNode{
        public char val;

        public TreeNode left;   //存储左孩子引用
        public TreeNode right;  //存储有孩子引用

        public TreeNode(char val) {
            this.val = val;
        }
    }

如图,我们利用上面创建的结点空间实现以下的二叉树。
在这里插入图片描述
代码如下:

    //手动创建一个二叉树,便于测试
    public TreeNode createTree(){
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');

        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        C.left = F;
        C.right = G;
        E.right = H;

        return A;   //返回树的根节点
    }

之后,我们要了解有关二叉树,需要实现的一些方法,如下:

1.实现二叉树的遍历(前、中、后序)。

2.实现获取树中结点的个数。

3.实现获取叶子结点的个数。

4.获取第 k 层元素个数。

5.获取二叉树的高度。

6.检测值为 val 的结点是否存在。

2.实现二叉树的遍历

简单解释:
我们知道二叉树的遍历有三种,分别为,前,中,后序。

前序:根 --> 左–> 右。

中序:左 --> 根 --> 右。

后序:左 --> 右 --> 根。

注:有关树的方法实现最主要的就是递归方法的使用!

代码实现:

    //前序遍历 根-->左-->右
    void preOrder(TreeNode root){
        if(root == null){
            return;
        }
        System.out.print(root.val+" ");
        preOrder(root.left);
        preOrder(root.right);
    }

    //中序遍历 左-->根-->右
    void inOrder(TreeNode root){
        if(root == null){
            return;
        }
        inOrder(root.left);
        System.out.print(root.val+" ");
        inOrder(root.right);
    }

    //后序遍历 左-->右-->根
    void postOrde(TreeNode root){
        if(root == null){
            return;
        }
        postOrde(root.left);
        postOrde(root.right);
        System.out.print(root.val+" ");
    }

详细解释:

这里,为了便于解释,我将上面的树进行了删减,以下图为例解释一下前序遍历的代码逻辑(其余两种遍历类似)。

在这里插入图片描述

在这里插入图片描述
结果为:ABDEC

3.实现获取树中结点的数量

简单解释:

获取树中的元素个数,最直接的方法就是定义一个计数器,通过遍历树中的元素实现累加。

代码实现:

    // 获取树中节点的个数
    public static int nodeSize = 0;
    public int size(TreeNode root){
        if(root == null){
            return 0;
        }
        //每次递归遍历时首先实现计数增加
        nodeSize++;
        size(root.left);
        size(root.right);
        return nodeSize;
    }

详细解释:
这里使用递归的方法,只要 root != null 优先实现对左树的遍历计数,然后在此基础上实现对右树元素进行遍历计数。最终返回个数和。

4.获取叶子结点的个数

简单解释:
叶子结点,就是指树中没有孩子结点的元素,也就是说,这样的节点左右均为 null 因此这也就成为了实现该方法的突破口。

代码如下:

    public int getLeafNodeCount2(TreeNode root){
        if(root == null){
            return 0;
        }
        if(root.left == null && root.right == null){
            return 1;
        }
		int tmp = getLeafNodeCount2(root.left) + getLeafNodeCount2(root.right);
		return tmp;
    }

详细解释:
同样这里使用了递归的方法,在递归前,先放出递归返回的条件,然后,进入递归,从整体看,将树分为左右两大部分,在进行细分,先让递归记录左边,在记录右边相加和,最终获得叶子结点的数量。

5.获取第K层元素的个数

简单解释:
在这里插入图片描述
如图所示:假设获取第 3 层元素数量。

对于 A 层,是第三层。对于 B,C 层,是第二层。对于 D,E,F,G 是第一层。
以此为判断条件,不难理解,可以在每次递归时让层数减少,当为 1 时就记录个数,最后返回即可。

代码实现:

    // 获取第K层节点的个数
    public int getKLevelNodeCount(TreeNode root,int k){
    	//这里需要注意当要查找的层数超过数高度的情况
        if(root == null || k <= 0){
            return 0;
        }
        if(k == 1){
            return 1;
        }
        int tmp = getKLevelNodeCount(root.left,k-1) + getKLevelNodeCount(root.right,k-1);
        return tmp;
    }

详细解释:
因为只需要找某一层的结点个数,因此,只要到该层节点时返回 1 ,如果继续 k-1 ,k = 0 就会返回 0 。同样的,这里递归需要将整棵树看做左右两大部分,在细分到下面的左右子树,最后加和返回该层的数量。

6.获取二叉树的高度

简单解释:
要获取高度,同样的需要对二叉树进行深入遍历,因为二叉树只有左右两部分,树的高度无非就是左右部分进行比较,这里将树看做左右两大部分,分别深入,最终进行比较即可。

代码实现:

    // 获取二叉树的高度
    public int getHeight(TreeNode root){
        if(root == null){
            return 0;
        }
        int leftHigh = getHeight(root.left);
        int rightHigh = getHeight(root.right);

        return leftHigh > rightHigh ? leftHigh+1 : rightHigh+1;
    }

详细解释:
同样,以 root 所指向为 null 为条件,将树分为左右两大部分,先对左树进行遍历,通过三目运算的判断,最终会返回出最长,高度最高的呢一串结点。

7.检测value值是否在该树中

简单解释:
同样,这个方法的实现也很简单,就是遍历整棵树,寻找对应的值后返回 root 即可。

代码实现:

    // 检测值为value的元素是否存在
    public TreeNode find(TreeNode root, int val){
        if(root == null){
            return null;
        }
        if(root.val == val){
            return root;
        }

        TreeNode ret1 = find(root.left,val);
        if(ret1 != null){
            return ret1;
        }
        TreeNode ret2 = find(root.left,val);
        if(ret2 != null){
            return ret2;
        }
        return null;
    }

详细解释:
与前面的方法相同,从整体上,将一棵树一分为二,首先,定义返回条件,这里需要定义两个结点来接受返回出来的目标值。只要最终返回的值符合条件,就将该地址传递出去即可,如果均不成立,呢没有找到目标值。

五、总结

简单总结:
在有关树的问题中,用的最多的就是递归的思想,这就需要我们用整体和部分的眼光来看问题,往往在深究深层是如何运行的时候常常会将问题复杂化,所以,子问题的思考方法在这里尤为重要!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值