序
数据结构大致可以分为两种类型:基于数组的实现与基于链表的实现。就其效率而言,二者各有长短。具体来说,前一实现方式允许我们通过下标或秩,在常数时间内找到目标对象;然而,一旦需要对这些结构进行修改,那么无论是插入还是删除,都需要耗费线性的时间。后者允许我们借助引用或位置对象,在常数的时间内插入或删除元素;但是为了找出居于特定次序的元素,我们不得不花费线性的时间,对整个结构进行遍历查找。
能否将这两类结构的优点结合起来,并避其不足?——树结构
在此之前介绍的向量、列表、栈和队列结构中,元素自火箭都存在一个自然的线性次序,所以它们均属于线性结构(linear structure
)。树则不然,其中的元素之间并不存在天然的直接后继或者直接前驱关系,但只要附加某种约束(比如遍历),也可以在树中的元素自火箭确定某种线性次序,因此树属于半线性结构(semi-linear structure
)。
树是一种分层结构,而层次化这一特征几乎蕴含于所有事物及其联系当中,成为其本质属性之一。作为树的特例,二叉树实际上并不失其一般性,无论是逻辑结构还是算法功能,任何有根有序的多叉树,都可等价地转化并实现为二叉树。本文重点分析二叉树。
1 、二叉树及其表示
1.1 树
- 有根树
从图论地角度看,树等价于连通无环图,因此与一般的图相同,树也由一组顶点以及联接与其间的若干条边(组成)。在计算机科学中,往往还会在此基础上,再指定某一特定顶点,并称之为根。在指定根结点之后,我们也称之为有根树(rooted tree)。此时,从程序实现的角度,我们也更多地将顶点称作节点。 - 深度与层次
沿每个节点v到根结点的唯一通路所经过边的数目,称作v的深度,规定根结点的深度为0,属于第0层。 - 祖先、后代与子树
任一节点v在通往树根沿途所经过的每个节点都是其祖先,v是它们的后代。特别地,v的祖先/后代包括其本身,而v本身以外的祖先/后代称作真祖先(proper ancestor)/真后代(proper descendant)。
节点v历代祖先的层次,自下而上以1为单位逐层递减;在每一层次上,v的祖先至多一个。特别地,若节点u是v地祖先且恰好比v高出一层,则称u是v的父亲(parent),v是u的孩子(child)。
v的孩子总数,称作其度数或度(degree),记作deg(v)。无孩子的节点称作叶节点(leaf),包括根在内的其余节点皆为内部节点(internal node)。v所有的后代及其之间的联边称作子树(subtree),记作subtree(v)。在不致歧义时,我们往往不再严格区分节点(v)及以之为根的子树(subtree(v))。 - 高度
树T中所有节点深度的最大值称作该树的高度(height),记作height(T).即,树的高度总是由其中某一叶节点的深度确定的,仅含单个节点的树高度为0,空树高度为-1.
1.2 二叉树
二叉树(binary tree)中每个节点的度数均不超过2.因此在二叉树中,同一父节点的孩子都可以左、右相互区分——此时,亦称作有序二叉树(ordered binary tree)。特别地,不含一度节点的二叉树称作真二叉树。
1.3 多叉树
一般地,树中各节点的孩子数目并不确定。每个节点的孩子均不超过k个的有根树,称作k叉树(k-ary tree)。
- 父节点
在多叉树中,根节点以外的任一节点有且仅有一个父节点。节点信息中保留父节点,常数时间内可以确定任一节点的父节点,但是孩子节点的查找时间消耗O(n)。 - 孩子节点
孩子节点的快速确定,但是父节点的查找消耗O(n)。 - 父节点 + 孩子节点
可以兼顾对父节点和孩子节点的定位,但是在节点插入与删除操作频繁的场合,为动态地维护和更新树的拓扑结构,必须反复遍历和调整一些节点所对应地孩子序列,消耗大量时间,影响整体效率。 - 有序多叉树 = 二叉树
解决以上难题地方法之一,就是采用支持高效动态调整地二叉树结构。为此,必须首先建立起从多叉树到二叉树地某种转换关系,并使得在此转换的意义下,任一多叉树都等价于某棵二叉树。当然,为了保证作为多叉树特例的二叉树有足够的能力表示任何一棵多叉树,我们只需给多叉树增加一项约束条件——同一节点的所有孩子之间必须具有某一线性次序。 - 长子 + 兄弟
有序多叉树中任一非叶子节点都有唯一的“长子”,而且从该“长子”出发,可按照预先约定或指定的次序遍历所有孩子节点。为此,对每个节点设置两个指针,分别指向其“长子”和下一“兄弟”。
2、编码树
以通讯编码算法的实现作为二叉树的应用实例。通讯理论中的一个基本问题是:如何在尽可能低的成本下,以尽可能高的速度,尽可能忠实地实现信息在空间和时间上的复制与转移。在现代通讯技术中,无论采用电、磁、光或其他任何形式,在信道上传递的信息大多数以二进制比特的形式表示和存在,而每一个具体的编码方案都对应于一棵二叉编码树。
2.1 二进制编码
在加载到信道上之前,信息被转换为二进制形式的过程称作编码(encoding);反之,经信道抵达目标后再由二进制编码恢复原始信息的过程称作解码(decoding)。
- 生成编码表
原始信息的基本组成单位称作字符,都来自于某一特定的有限集合,即字符集
。而以二进制形式承载的信息,都可表示为来自编码表的某一特定二进制串。每一编码表都是从字符集到编码表的一个单射,编码就是对信息文本中各字符逐个实施这一映射的过程,而解码则是逆向映射的过程。 - 二进制编码
为避免出现歧义编码(比如11——M,111——N,当出现11111,解码多种可能),采用“前缀无歧义编码”(prefix-f