算法入门之树简介
像我们熟知的栈、队列、散列表等这些都是一对一存储称为线性结构,但在现实生活中不可避免的会用到一对多的场景,基于这些场景聊聊这些一对多的解决方案,树或者图这些都是非线性存储。
树在现实生活中给我们的印象是一条主干多条分支,这些分支上再去延申在其末端长出树叶,如下。
在数据结构中同样有类似定义,那么如何定义数据结构中的树呢?
树的相关概念
树是n(n>=0)个节点的有限集,同样还具备如下特征
-
当n为0时,这个有限集被称为空树。
-
当n>=1时有且只有一个根节点。
-
当n>1时,其余节点可分为m(m>0)个互不相交的有限集,每个集又是一棵树,称为根的子树。
树结构图如下所示
在树的顶端存在唯一一个节点,这个节点被称为根节点,树中没有子节点的节点被称为叶子节点,反之被称为非叶子节点,树的层级即表示树的高度,如图例显示树的高度为4。
如上图节点4的上级节点也就是2被称为节点4的父节点(parent),而节点5被称为节点4的兄弟节点(sibling),节点7、8、9被称为节点4的子节点(child)。
二叉树
树有很多变种,其中二叉树就是一种特殊的形式,二叉顾名思义就是一个节点最多包含两个子节点,如下
而二叉树中又衍生出两种形式,满二叉树和完全二叉树,这两种树很相似又稍有不同。
满二叉树
满二叉树是在二叉树的基础上新增了一个限定条件:
一个二叉树的所有非叶子节点都存在左右孩子节点,所有的叶子节点都存在同一个层级上。
所以满二叉树展示如下
满二叉树其实就是所有的非叶子节点都包含左右孩子节点,而完全二叉树就没有那么严格。
完全二叉树
完全二叉树在二叉树的基础上满足限定条件:
对于有n个节点的二叉树,将这n个节点按照层级关系由上到下编号,如果这个树的所有节点和相同深度的满二叉树从1到n节点位置相同,那么这个树被称为完全二叉树。
二叉树的存储
二叉树其底层可以采用数组和链表实现,但由于是一对二的关系,所以存储结构上会有差别。
链表存储
链表存储其实很好理解,其实就是由原来的next指针变为了左右孩子指针,结构图如下
代码定义结构如下
/**
* 树节点定义
*/
class Node{
// 存储数据
Object data;
// 左孩子节点
Node left;
// 右孩子节点
Node right;
public Node(Object data){
this.data = data;
this.left = null;
this.right = null;
}
}
数组存储
数组为线性存储那么如何表达一个非线性结构呢?
按照层级顺序将二叉树的各个节点存放到对应位置,如果出现左右孩子节点为空的情况,需要空节点的位置空闲出来,数组存储映射如下。
那么我们如何确定一个节点左右孩子节点的具体位置呢?
如果按照数组下标来进行统计,那么上面树的节点对应下标如下
很容易可以看出如果某个节点的下标为n,那么左孩子节点数组下标为(2 * n+1),而右孩子节点数组下标为(2 * n+2)。
同样如果知道左孩子节点下标或者右孩子节点下标也可以类推出父节点下标。