带着问题学习是最有效的学习方式之一。
今天的问题是:二叉树有哪几种存储方式?什么样的二叉树适合用数组来存储?
树
首先来看什么是“树"?再完备的定义,都没有图直观。所以通过下图来看看这些树有什么特征?
”树“这种数据结构很像现实中的树,里面每个元素叫做“节点”;用来连线相邻节点之间的关系叫做”父子关系“。
下面这幅图,A节点就是B节点的父节点,B节点是A节点的子节点,B、C、D三个节点的父节点是同一个节点,因此它们之间称之为兄弟节点。把没有父节点的节点叫做根节点,也就图中的节点E。把没有子节点的节点叫做叶子节点或者叶节点。G、H、I、J、K、L都是叶子节点。。。
除此之外还有三个比较相似的概念:高度、深度、层,定义是这样的:
这三个概念定义比较容易混淆,描述起来也比较空洞。举个例子来说明一下:
二叉树(Binary Tree)
满二叉树
完全二叉树
满二叉树很好理解也很好识别,但是完全二叉树有些人就分不清楚了,可以通过下图来对比看看:
为什么偏偏最后把最后一层的叶子节点靠左排列的叫完全二叉树?如果靠右排列就不能叫完全二叉树了吗?这个定义由来或者说目的在哪里?
要理解完全二叉树定义的由来,需要先了解如何表示(或者存储)一颗二叉树???
存储一颗二叉树有两种方法:一种是基于指针或者引用的二叉链式存储法,一种是基于数组的顺序存储法。。。
比较简单、直观的就是链式存储法
- 链式存储法
从图中可以清楚看到每个节点有三个字段,其中一个存储数据,另外两个是指向左右子节点的指针。只要拎住根节点就可以通过左右子节点的指针,把整棵树都串起来,这种存储方式比较常用,大部分二叉树代码都是通过这种结构实现的。
下面来看看基于数组的顺序存储法。把根节点存储在下标i=1的位置,那左子节点存储在下标2i=2的位置, 右子节点存储在2i+1=3的位置。以此类推,B节点的左子节点存储在2 * i = 2 * 2 = 4 的位置,右子节点存储在 2 * i + 1 = 2 * 2 + 1 = 5 的位置。
刚刚举的例子是一颗完全二叉树,所以仅仅浪费了一个下标为0的存储位置。如果是非完全二叉树,其实会浪费比较多的数组存储空间,可以看一下下面的例子:
当讲到推和堆排序的时候,会发现堆其实就是一种完全二叉树,最常用的存储方式局势数组。。
二叉树的遍历
经典的遍历方法有三种,前序、中序和后序。
- 前序遍历:对于树中任意节点来说先打印这个点,再打印左子树之后右子树。
- 中序遍历:对于树中任意节点来说先打印左子树,再打印本身之后右子树。
- 后序遍历:对于树中任意节点来说先打印左子树,再打印右子树之后是本身。
实际上二叉树的前、中、后序遍历就是一个递归的过程。
二叉树遍历的时间复杂度是O(n)。。。