Java数据结构:树(待完善)

6. 树

树的特点:

  1. 两个节点仅有唯一的一条路径联通
  2. 一棵树有n个节点,则一定有n-1条边
  3. 树中没有回路

二叉树示例图

  • 节点 :树中的每个元素都可以统称为节点。
  • 根节点 :顶层节点或者说没有父节点的节点。上图中 A 节点就是根节点。
  • 父节点 :若一个节点含有子节点,则这个节点称为其子节点的父节点。上图中的 B 节点是 D 节点、E 节点的父节点。
  • 子节点 :一个节点含有的子树的根节点称为该节点的子节点。上图中 D 节点、E 节点是 B 节点的子节点。
  • 兄弟节点 :具有相同父节点的节点互称为兄弟节点。上图中 D 节点、E 节点的共同父节点是 B 节点,故 D 和 E 为兄弟节点。
  • 叶子节点 :没有子节点的节点。上图中的 D、F、H、I 都是叶子节点。
  • 节点的高度 :该节点到叶子节点的最长路径所包含的边数。
  • 节点的深度 :根节点到该节点的路径所包含的边数
  • 节点的层数 :节点的深度+1。
  • 树的高度 :根节点的高度。

图片和概念定义来源于JavaGuide

二叉树(Binary tree)的分类

二叉树是每个节点最多有两个分支的树结构

二叉树的分支通常称为“左子树”和”右子树“,分支具有左右次序,不能随意颠倒

二叉树的第 i 层至多拥有 2^(i-1) 个节点,深度为 k (层数为k+1)的二叉树至多总共有 2^(k+1)-1 个节点(满二叉树的情况),至少有 2^(k) 个节点

满二叉树

满二叉树是每一层节点数都达到最大值的二叉树,如果一个二叉树的层数为 K,且结点总数是(2^k) -1 ,则它就是满二叉树

完全二叉树

完全二叉树除最后一层其余层全满,同时,它的最后一层是满的,或者是右边缺少若干连续节点(扩展完左边才能扩展右边)

完全二叉树的父节点和子节点的序号有对应关系:

  • 当根节点的值为1是,若父结点的序号是 i,那么左子节点的序号就是 2i,右子节点的序号是 2i+1

平衡二叉树

平衡二叉树具有以下性质:

  1. 可以是空树
  2. 如果不是空树,它的左右两个子树的高度差的绝对值不超过 1,并且左右两个子树都是一棵平衡二叉树。

平衡二叉树的常用实现方法有 红黑树AVL 树替罪羊树加权平衡树伸展树等。

红黑树

红黑树特点 :

  1. 每个节点非红即黑;
  2. 根节点总是黑色的;
  3. 每个叶子节点都是黑色的空节点(NIL节点);
  4. 如果节点是红色的,则它的子节点必须是黑色的(反之不一定);
  5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。

红黑树的应用 :TreeMap、TreeSet以及JDK1.8的HashMap底层都用到了红黑树。

二叉查找树(Binary Search Tree, BST)

左子节点的值比父节点的值要小,右节点的值要比父节点的值大的二叉树。

它的深度决定了它的查找效率。

执行赋值度:

  • 增删查改:理想时为O(logN),最坏情况为O(N)

斜树

退化为链表结构

二叉树的存储

链式存储

和链表类似,二叉树的链式存储依靠指针将各个节点串联起来,不需要连续的存储空间。

每个节点包括三个属性:

  • 数据 data。data 不一定是单一的数据,根据不同情况,可以是多个具有不同类型的数据。
  • 左节点指针 left
  • 右节点指针 right

指针引用对象

顺序存储

顺序存储就是利用数组进行存储,数组中的每一个位置仅存储节点的 data,不存储指针,子节点的索引通过数组下标完成。

根结点的序号为 1,对于每个节点 Node,假设它存储在数组中下标为 i 的位置,那么它的左子节点就存储在 2_i 的位置,它的右子节点存储在下标为 2_i+1 的位置。

若存储的二叉树不是完全二叉树,在数组中就会出现空隙,导致内存利用率降低

二叉树的遍历

先序遍历

根在前,从左往右,一棵树的根永远在左子树前面,左子树又永远在右子树前面

二叉树的先序遍历

  1. 先输出根结点
  2. 再遍历左子树
  3. 最后遍历右子树

遍历左子树和右子树的时候,同样遵循先序遍历的规则。

可以通过递归实现先序遍历。

public void preOrder(TreeNode root){
    if(root == null){
        return;
    }
    system.out.println(root.data);
    preOrder(root.left);
    preOrder(root.right);
}

中序遍历

根在中,从左往右,一棵树的左子树永远在根前面,根永远在右子树前面

中序遍历的顺序:

  1. 先递归中序遍历左子树
  2. 再输出根结点的值
  3. 再递归中序遍历右子树
public void inOrder(TreeNode root){
    if(root == null){
        return;
    }
    inOrder(root.left);
    system.out.println(root.data);
    inOrder(root.right);
}

后序遍历

根在后,从左往右,一棵树的左子树永远在右子树前面,右子树永远在根前面

后序遍历的顺序:

  1. 先递归后序遍历左子树
  2. 再递归后序遍历右子树
  3. 最后输出根结点的值
public void postOrder(TreeNode root){
    if(root == null){
        return;
    }
    postOrder(root.left);
    postOrder(root.right);
    system.out.println(root.data);
}
posted @ 2022-03-23 16:08  chachan53  阅读(71)  评论(0编辑  收藏  举报