大话数据结构-树( 三 )


1) 叶子只能出现在最下一层 , 出现在其他层就不可能达到平衡;
2) 非叶子结点的度一定是2;
3) 在同样深度的二叉树中 , 满二叉树的结点个数最多 , 叶子数最多;
(3) 完全二叉树:对一棵具有n个结点的二叉树按层序编号 , 如果编号为i(1 = 1) 。
性质三:对任何一棵二叉树T , 如果其终端结点数为n0 , 度为2的结点数为n2 , 则n0 = n2 + 1 。
性质四:具有n个结点的完全二叉树的深度为(int)(log2n) + 1 。
性质五:如果对一棵有n个结点的完全二叉树(其深度为(int)(log2n) + 1)的结点按层序编号(从第1层到第(int)(log2n) + 1层 , 每层从左到右) , 对任一结点i(1n , 则结点i无左孩子(结点i为叶子结点);否则其左孩子是结点2i;
(3) 如果2i + 1 > n , 则结点i无右孩子;否则其右孩子是结点2i + 1;
7.4 二叉树的顺序存储结构
二叉树的顺序存储结构 , 就是用一维数组来存储二叉树的结点 , 并且存储的位置 , 也就是数组的下标要能体现结点之间的逻辑关系 , 比如双亲与孩子的关系 , 左右兄弟的关系 , 如果出现无左孩子或右孩子时 , 则空置一个数组元素位 , 如:
表示为:
假设数组为array , 根据上文描述的二叉树特性 , 可知 , array[0]为根结点 , 因为数组的下标是从0开始的 , 所以实际上 , 这是树上的第1个元素 , 那么 , array[0]的左子结点是第2 * 1个元素 , 其右结点是2 * 1 + 1个元素 , 即左结点是array[1] , 右结点是array[2] 。同理 , 根据特性还可知 , 下标为5 , 即第6个元素存储的结点的父结点是第6/2个结点 , 即下标为2(数组的下标减1 , 即6/2 - 1)的结点 。
当然 , 这样计算过于复杂了 , 如果完全按数组的下标计算的话 , 应是:
(1) 下标为i的结点的左子结点的下标为2 * i + 1 , 右子结点的下标为2 * i + 2;
(2) 下标为i的结点的双亲结点下标为(int)((i - 1) / 2);
继续看 , 下面二叉树:
注意 , 结点C没有左子结点 , 此时 , 在进行顺序存储时 , C的左子结点位置应存储一个空值:
而如下树:
存储结构则为:
可见 , 在使用数组对二叉树进行存储时 , 数组每个元素应存储的结点 , 是按完全二叉树设计的 , 但凡完全二叉树中有一个结点不存在时 , 此结点对应的数组位置应存储一个空值 。
这种存储结构 , 只有在针对完全二叉树时 , 才不会浪费空间 , 否则都会造成一定的空间浪费 。
7.5 二叉树的链式存储结构
既然顺序存储适用性不强 , 我们就要考虑链式存储结构 , 二叉树每个结点最多有两个孩子 , 所以为它设计一个数据域和两个指针域即可 , 这样的链表叫做二叉链表 。如果添加一个指向其双亲的指针域 , 则称为三叉链表 。
import lombok.Data;import java.util.UUID;/*** 二叉树结点** @author Korbin* @date 2023-01-18 17:46:55**/@Datapublic class BinaryTreeNode {/*** 数据域**/private T data;/*** id , 用于判断是否相等时使用**/private String id = UUID.randomUUID().toString();/*** 左子结点**/private BinaryTreeNode leftChild;/*** 右子结点**/private BinaryTreeNode rightChild;/*** 判断节点是否相等** @param node 待判断的结点* @return 相等返回true , 否则返回false* @author Korbin* @date 2023-01-19 17:22:46**/public boolean equals(BinaryTreeNode node) {return node.getData().equals(data) && node.getId().equals(id);}}