Java数据结构:树(待完善)
6. 树
树的特点:
- 两个节点仅有唯一的一条路径联通
- 一棵树有n个节点,则一定有n-1条边
- 树中没有回路
- 节点 :树中的每个元素都可以统称为节点。
- 根节点 :顶层节点或者说没有父节点的节点。上图中 A 节点就是根节点。
- 父节点 :若一个节点含有子节点,则这个节点称为其子节点的父节点。上图中的 B 节点是 D 节点、E 节点的父节点。
- 子节点 :一个节点含有的子树的根节点称为该节点的子节点。上图中 D 节点、E 节点是 B 节点的子节点。
- 兄弟节点 :具有相同父节点的节点互称为兄弟节点。上图中 D 节点、E 节点的共同父节点是 B 节点,故 D 和 E 为兄弟节点。
- 叶子节点 :没有子节点的节点。上图中的 D、F、H、I 都是叶子节点。
- 节点的高度 :该节点到叶子节点的最长路径所包含的边数。
- 节点的深度 :根节点到该节点的路径所包含的边数
- 节点的层数 :节点的深度+1。
- 树的高度 :根节点的高度。
二叉树(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,并且左右两个子树都是一棵平衡二叉树。
平衡二叉树的常用实现方法有 红黑树 、 AVL 树 、 替罪羊树 、 加权平衡树 、伸展树等。
红黑树
红黑树特点 :
- 每个节点非红即黑;
- 根节点总是黑色的;
- 每个叶子节点都是黑色的空节点(NIL节点);
- 如果节点是红色的,则它的子节点必须是黑色的(反之不一定);
- 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。
红黑树的应用 :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 的位置。
若存储的二叉树不是完全二叉树,在数组中就会出现空隙,导致内存利用率降低
二叉树的遍历
先序遍历
根在前,从左往右,一棵树的根永远在左子树前面,左子树又永远在右子树前面
二叉树的先序遍历:
- 先输出根结点
- 再遍历左子树
- 最后遍历右子树
遍历左子树和右子树的时候,同样遵循先序遍历的规则。
可以通过递归实现先序遍历。
public void preOrder(TreeNode root){
if(root == null){
return;
}
system.out.println(root.data);
preOrder(root.left);
preOrder(root.right);
}
中序遍历
根在中,从左往右,一棵树的左子树永远在根前面,根永远在右子树前面
中序遍历的顺序:
- 先递归中序遍历左子树
- 再输出根结点的值
- 再递归中序遍历右子树
public void inOrder(TreeNode root){
if(root == null){
return;
}
inOrder(root.left);
system.out.println(root.data);
inOrder(root.right);
}
后序遍历
根在后,从左往右,一棵树的左子树永远在右子树前面,右子树永远在根前面
后序遍历的顺序:
- 先递归后序遍历左子树
- 再递归后序遍历右子树
- 最后输出根结点的值
public void postOrder(TreeNode root){
if(root == null){
return;
}
postOrder(root.left);
postOrder(root.right);
system.out.println(root.data);
}