数据结构之二叉树

在介绍二叉树之前,先说一下树的结构和几个容易混淆的小知识点。
树的结构:
在这里插入图片描述
怎么判断它是不是一棵树呢?
1)子树不相交
2)除了根节点以外,每个节点有且仅有一个父节点
3)一棵N个节点的树有N-1条边
关于树里边节点、父节点、兄弟节点、孩子等概念不多叙述,注意以下几个概念:
1)节点的度:一个节点含有子树的个数称为该节点的度,如:A的度为6
2)叶子节点:度为0的节点,如B,C,H等
3)树的度:一棵树中,最大的节点的度称为树的度,如:上图中树的度为6
4)树的深度:树中所有节点的层次的最大值称为该树的深度,如:上图中树的深度为4

二叉树

1、二叉树的特点

1)每个节点最多有两棵子树,即二叉树不存在度大于2的节点。
2)二叉树的子树有左右之分,其子树的次序不能颠倒。

2、二叉树的结构

在这里插入图片描述

3、特殊的二叉树

1)满二叉树:一个二叉树,如果每一层的节点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且节点总数为(2^k)-1,它就是满二叉树。
2)完全二叉树:对于深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中编号从1至n的节点一一对应时就称为完全二叉树。(我的个人理解是,如果一个节点没有左孩子,而有右孩子,那它一定不是完全二叉树!)
满二叉树是一种特殊的完全二叉树。
在这里插入图片描述

4、二叉树的存储结构

4.1 顺序存储
顺序结构存储就是使用数组来存储,一般只适用于表示完全二叉树,否则会有空间的浪费。
现实中只有堆才会使用数组来存储。
堆的特点:

  • 堆中某个节点的值总是不大于或不小于其父节点的值
  • 堆总是一棵完全二叉树

堆在笔试中经常考到,向下调整和向上调整算法,这个在之后的一篇文章里会详细介绍。

4.2 链式存储
链式存储就是使用链表来表示一棵二叉树。链表中每个节点由三部分组成,数据域和左右指针域,左右指针域分别用来保存该节点的左孩子和右孩子所在的节点的存储地址。
二叉树链式结构的遍历:
1)前序遍历:先访问根节点,再访问左子树,最后访问右子树。
2)中序遍历:先访问左子树,再访问根节点,最后访问右子树。
3)后序遍历:先访问左子树,再访问右子树,最后访问根节点。
4)层序遍历:从根节点所在层开始,从左到右访问每一层的节点(自上而下,自左至右)。
示例:
在这里插入图片描述
代码实现:
前序遍历:使用递归算法实现,先打印根节点的值,然后递归遍历左子树,接着遍历右子树。

private static void preOrderTraversal(Node root) {
        if (root != null) {
            //根 左子树的前序遍历 右子树的前序遍历
            System.out.print(root.value + " ");
            preOrderTraversal(root.left);
            preOrderTraversal(root.right);
        }
    }

中序遍历后序遍历的代码实现思路和前序遍历一样,不过是顺序不同,中序遍历是“左根右”,后序遍历是“左右根”。直接看代码:

private static void inOrderTraversal(Node root) {
        if (root != null) {
            //左子树的中序遍历 根 右子树的中序遍历
            inOrderTraversal(root.left);
            System.out.print(root.value + " ");
            inOrderTraversal(root.right);
        }
    }

    private static void postOrderTraversal(Node root) {
        if (root != null) {
            //左子树的后序遍历 右子树的后序遍历 根
            postOrderTraversal(root.left);
            postOrderTraversal(root.right);
            System.out.print(root.value + " ");
        }
    }
二叉树的性质
  • 若规定根节点的层数是1,则一棵非空二叉树的第 i 层上最多有 2i-1(i>0)个节点。
  • 若规定只有根节点的二叉树的深度为1,则深度为k的二叉树的最大节点数是2k-1(k>0)。
  • 对任何一棵二叉树,如果其叶子节点个数为n0,度为2的非叶子节点个数为n2,则有n0=n2+1。
  • 具有n个节点的完全二叉树的深度k为log2(n+1)上取整。
  • 对于具有n个节点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的节点有:
    1)若 i>0,双亲序号:(i-1)/2。i=0,i为根节点,无双亲
    2)若 2i+1<n,左孩子序号:2i+1,否则无左孩子
    3)若 2i+2<n,右孩子序号:2i+2,否则无右孩子
二叉树的基础操作

1、求二叉树节点的个数
思路:
有两种解决方法,一种是利用遍历的思想,先累加根节点的个数,然后递归遍历左子树,接着递归遍历右子树,和前中后序遍历的思路是一样的。

private static int count = 0;
    private static int countByTraversal(Node root) {
        if (root != null) {
            count++;
            countByTraversal(root.left);
            countByTraversal(root.right);
        }
        return count;
    }

另一种方法是用子问题的思想解决,采用递归的方法,先算出左子树有多少节点,再计算右子树的节点数,最后加上1(根节点),如果根节点为空,说明该二叉树没有节点,如果左右子树同时为空,说明该二叉树只有一个节点。
在这里插入图片描述
代码如下:

private static int count(Node root) {
        if (root == null) {
            return 0;
        } else if (root.left == null && root.right == null) {
            //可选
            return 1;
        } else {
            return count(root.left) + count(root.right) + 1;
        }
    }

2、求叶子节点个数
思路:
方法一:遍历的思想
还是和前中后序遍历的思路相同,所谓叶子节点,就是度为0的节点,只有左右子树都为空的时候才累加节点个数。

private static int getLeafSize(Node root){
        if (root!=null){
            if (root.left==null && root.right==null){
                count++;
            }
            getLeafSize(root.left);
            getLeafSize(root.right);
        }
        return count;
    }

方法二:子问题的思想
先计算左子树的叶子节点个数,再计算右子树的叶子节点个数,最后相加,如果根节点为空,说明二叉树为空,不存在节点自然叶子节点数为0,而如果左右子树同时为空,说明根节点就是叶子节点。
在这里插入图片描述
代码如下:

private static int getLeafSize2(Node root){
        if (root==null){
            return 0;
        }else if (root.left==null && root.right==null){
            return 1;
        }else {
            return getLeafSize2(root.left)+getLeafSize2(root.right);
        }
    }

3、求二叉树的高度
思路:
利用递归的思想,先求出左子树的高度,再求出右子树的高度,然后将二者中较大的一个加1(根节点所在层)就是这棵二叉树的高度。如果根节点为空,该二叉树不存在,高度为0,如果左右子树同时为空,说明该二叉树只有根节点这一层,高度为1。
直接看代码:

private static int height(Node root){
        //空树
        if (root==null){
            return 0;
        }else if (root.left==null && root.right==null){
            //一个节点,可选
            return 1;
        }else {
            //其它:max(left,right)+1
            int left = height(root.left);
            int right = height(root.right);
            return (left>right?left:right)+1;
        }
    }

4、求第k层节点个数
思路:
如果根为空,说明该二叉树不存在,返回0,如果k为1,就是求第1层的节点个数,返回1(根节点),其余层的节点个数是左子树第k-1层的个数加上右子树第k-1层的个数。
(注意:求k-1层的个数是因为第k层相对于根的左右子树就是第k-1层)
直接看代码:

private static int getKLevelSize(Node root,int k){
        if (root==null){
            return 0;
        }else if (k==1){
            return 1;
        }else {
            return getKLevelSize(root.left,k-1)+getKLevelSize(root.right,k-1);
        }
    }

5、依次在二叉树的根、左子树、右子树 中查找value,如果找到,返回结点,否则返回null
思路:
如果根为空,说明该二叉树不存在,返回null,如果根的值等于value,直接返回根节点。其他情况,依然是递归的思想,先去左子树遍历查找,找到了就返回该节点,找不到再去右子树查找。
代码如下:

private static Node find(Node root,char v){
        //树为空
        if (root==null){
            return null;
        }
        //根节点是要查找的节点
        if (root.value==v){
            return root;
        }
        //去左子树查找
        Node r = find(root.left,v);
        if (r!=null){
            return r;   //返回左子树中找到的节点引用
        }
        //左子树没找到,去右子树查找
        return find(root.right,v);
    }

完整代码链接:
https://github.com/Nanfeng11/DataStructure/blob/master/BinaryTree/src/main/java/com/nanfeng/BinaryTree.java

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值