Java数据结构 二叉树

二叉树概述

一、树型结构(了解)
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:

  • 有一个特殊的节点,称为根节点,根节点没有前驱节点;
  • 除根节点外,其余节点被分成M(M > 0)个互不相交的集合T1、T2、…、Tm,其中每一个集合Ti (1 <= i <= m) 又是一棵与树类似的子树。每棵子树的根节点有且只有一个前驱,可以有0个或多个后继;
  • 树是递归定义的。
    在这里插入图片描述

除此以外还有如下定义:

  • 节点的度:一个节点含有的子树的个数称为该节点的度,度为0的节点叫叶子节点,度不为0的节点叫分支节点,如上图:A的度为6;
  • 树的度:一棵树中,最大的节点的度称为树的度,如上图:树的度为6;
  • 叶子节点或终端节点:度为0的节点称为叶节点,如上图:B、C、H、I…等节点为叶节点;
  • 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点, 如上图:A是B的父节点;
  • 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;,如上图:B是A的孩子节点;
  • 根结点:一棵树中,唯一没有父结点的结点,如上图:A;
  • 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  • 树的高度或深度:树中节点的最大层次或最大的节点深度, 如上图:树的高度为4;

二、二叉树
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
特点:
1.每个结点最多有两棵子树,即二叉树不存在度大于 2 的结点;
2.二叉树的子树有左右之分,其子树的次序不能颠倒,因此二叉树是有序树。

两种特殊的二叉树:
1.满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树;在这里插入图片描述

2.完全二叉树:一个节点若存在右树,则一定存左树 ;
在这里插入图片描述
三、二叉树的性质
1.若规定根节点的层数为1,则第i层上最多有2^(i-1) (i>0)个节点,即满二叉树;
2.若规定只有根节点的二叉树的深度为1,则深度为K的二叉树的最大结点数是2^K-1 (K>0);
3.对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+1;
4.关于完全二叉树的编号问题,若规定根节点编号为0,则对于一个编号为i的节点来说有:

  • 若i>0,父节点编号:(i-1)/2;i=0,i为根节点编号,无父节点;
  • 若2i+1<n,左孩子编号:2i+1,否则无左孩子;
  • 若2i+2<n,右孩子编号:2i+2,否则无右孩子;

四、二叉树的存储
二叉树的存储结构分为:顺序存储和类似于链表的链式存储。

二叉树的链式存储是通过一个一个的节点引用起来的,常见的表示方式有二叉和三叉表示方式,具体如下:

//孩子表示法
class Node {
    int val; // 数据域
    Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
    Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
}
// 孩子双亲表示法
class Node {
    int val; // 数据域
    Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
    Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
    Node parent;    // 当前节点的根节点
}

五、二叉树的遍历
遍历:按照一定的顺序访问这个集合的元素,做到不重复,不遗漏。

  • NLR:前序遍历(Preorder Traversal 亦称先序遍历)——先访问根节点,再递归访问左子树,最后递归访问右子树,即根左右;在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • LNR:中序遍历(Inorder Traversal)——先递归访问左子树,再访问根节点,最后递归访问右子树,即左根右;
    在这里插入图片描述

  • LRN:后序遍历(Postorder Traversal)——先递归访问左子树,再递归访问右子树,最后访问根节点;
    在这里插入图片描述
    在这里插入图片描述

1.先序遍历的第一个访问的是根节点,后序遍历最后一个访问的是根节点;
2.中序遍历和后序遍历访问的第一个节点是当前树的最左侧节点;
3.中序遍历中,左子树的遍历结果都在根节点的左侧,右子树的遍历结果都在根节点右侧;
4.给定二叉树的前中后遍历结果,根据这三个结果确定二叉树结构:前中遍历或中后遍历可以,前后遍历不可以;

六、二叉树的基本操作
在这里插入图片描述

1.生成一个测试二叉树

// 孩子兄弟表示法
    private static class Node {
        private char val;
        private Node left;
        private Node right;
        public Node(char val) {
            this.val = val;
        }
    }

    // 生成一个测试二叉树
    public Node buildATree() {
        Node nodeA = new Node('A');
        Node nodeB = new Node('B');
        Node nodeC = new Node('C');
        Node nodeD = new Node('D');
        Node nodeE = new Node('E');
        Node nodeF = new Node('F');
        Node nodeG = new Node('G');
        Node nodeH = new Node('H');
        nodeA.left = nodeB;
        nodeA.right = nodeC;
        nodeB.left = nodeD;
        nodeB.right = nodeE;
        nodeE.left = nodeG;
        nodeG.right = nodeH;
        nodeC.right = nodeF;
        return nodeA;
    }

2.先序遍历

/**
     * 传入一个二叉树的根节点root,根据先序遍历访问该树
     * 左根右
     * @param root 传入的根节点
     */
    public void preOrder(Node root) {
        if (root == null) {
            return;
        }
        // 根
        System.out.print(root.val +" ");
        // 左树
        preOrder(root.left);
        // 右树
        preOrder(root.right);
    }

3.中序遍历

/**
     * 传入一个二叉树的根节点root,根据中序遍历访问该树
     * 左根右
     * @param root 传入的根节点
     */
    public void inOrder(Node root) {
        if (root == null) {
            return;
        }
        // 先按照中序遍历访问左子树
        inOrder(root.left);
        System.out.print(root.val + " ");
        // 按照中序再访问右子树
        inOrder(root.right);
    }

4.后序遍历

/**
     * 按照后序遍历访问二叉树
     * @param root
     */
    public void postOrder(Node root) {
        if (root == null) {
            return;
        }
        // 先左
        postOrder(root.left);
        // 再右
        postOrder(root.right);
        System.out.print(root.val +" ");
    }

5.求二叉树节点个数

/**
     * 传入一个二叉树的根节点,求出该二叉树的节点个数
     * @param root 二叉树的根节点
     * @return
     */
    public int getNodeCount(Node root) {
        if (root == null) {
            return 0;
        }
        return 1 + getNodeCount(root.left) + getNodeCount(root.right);
    }

6.求二叉树叶子节点个数

/**
     * 传入一个二叉树根节点root,就能求得该二叉树的叶子节点个数
     * @param root 传入根节点
     * @return
     */
    public int getLeafNodeCount(Node root) {
        // 空树
        if (root == null) {
            return 0;
        }
        // 只有根节点
        if (root.left == null && root.right == null) {
            return 1;
        }
        // 此时二叉树不为空且有子树
        // 此时二叉树叶子节点个数 = 左树叶子节点个数 + 右树的叶子节点个数
        return getLeafNodeCount(root.left)
                + getLeafNodeCount(root.right);
    }

7.求二叉树的高度(深度)

/**
     * 传入一个根节点root,就能求得该树高度
     * @param root 传入二叉树根节点
     * @return
     */
    public int height(Node root) {
        if (root == null) {
            return 0;
        }
        // 左树高度 height(root.left) -> 以root.left为根节点的高度
        // 二叉树不为空 1 + Math.max(左树高度,右树高度);
        return 1 + Math.max(height(root.left),height(root.right));
    }

8.判断二叉树中是否包含指定节点

/**
     * 判断一颗以root为根节点的二叉树中是否包含值为val的节点
     * @param root
     * @param val
     * @return
     */
    public boolean contains(Node root,char val) {
        if (root == null) {
            return false;
        }
        if (root.val == val) {
            return true;
        }
        // 在左树或右树中寻找val
        return contains(root.left,val) || contains(root.right,val);
    }

9.求二叉树第k层的节点个数

/**
     * 传入一个二叉树的根节点root,求出第k层节点个数
     * @param root
     * @param k
     * @return
     */
    public int getKLevelNodes(Node root,int k) {
        if (k <= 0 || root == null) {
            return 0;
        }
        if (k == 1) {
            return 1;
        }
        return getKLevelNodes(root.left,k - 1) +
                getKLevelNodes(root.right,k - 1);
    }
  • 2
    点赞
  • 4
    收藏
  • 打赏
    打赏
  • 1
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:游动-白 设计师:我叫白小胖 返回首页
评论 1

打赏作者

_YQ97

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值