线性关系(线性结构):是指在数据元素的非空有限集合中有且仅有一个首元素的数据元素,有且只有一个尾元素的数据元素,其余的数据元素均有且只有一个直接前驱元素和一个直接后继元素
常见的线性结构:线性表,队列,栈,串,数组。
非线性结构:线性结构的元素之间具有线性关系,非线性结构中的元素之间不再是序列的关系,他们呈现的是更复杂的层次关系,即一个数据元素有且仅有一个直接前驱,但可有另个或者多个直接后继,显然比序列关系复杂
常见非线性结构:树,图
散列表
PHP中的hashtable就是哈希表就是由数组和链表组成,一个长度为16的数组中,每个元素存储的是一个链表的头结点。而HashMap和Hashtable就是哈希表结构
表(list)
操作PrintList,Find,Delete,Insert
1.使用数组(array)实现
定义一个数组的时候,通常需要对表的大小进行估计(因为数组的元素之间物理地址是相连续的,所以需要一次性分配),一般需要估计的大一些,自然会浪费掉一些空间。
PrintList和Find肯定是跟表的大小是相关的,也就是线性的。FindKth的花费就是常数时间
Insert和Delete的代价很大。比如在表的位置0插入(Insert)一个新元素,那么整个数组的元素都要后移一位。删除第一个元素也需要将后面的所有元素都向前移动一位。
所以表的结构使用数组来实现显然不是好的选择
2.使用链表(linked list)实现
链表是由一系列不必在内存中中相连的结构组成,每个结构都含有表元素和一个执行包含该元素的后继元素物理地址的结构的指针,称之为next指针。最后一个单元的next指针为NULL
PrintList(L)和Find(L,Key),我们只要将一个指针传递到该表的第一个元素,然后用一些next指针贯穿该表即可,花费时间是跟表的大小线性相关的。
FindKth(L,i)花费时间O(i)
Delete操作只是将被删除元素的前驱元素的next指针修改为被删除元素的next指针就可以了
Insert元素只需要一次malloc从系统申请一个新单元并且执行两次指针调整就可以了
树
对于大量的输入数据,链表的线性访问时间太慢。树的大部分操作的运行时间平均为O(logN)。一棵树是由N个节点和N-1条边的集合,其中一个节点是根结点,每条边都将某个节点连接到它的父亲,而除去根结点外每一个节点都有一个父亲
树的存储实现:
结点即为数据元素,存储比较简单和直接
分支描述的是关系,关系的存储是复杂而多样的,因此需要从存储分支信息的角度入手分析可能的解决方案。
1.双亲数组表示法
因为一条分支描述的是相邻两个结点(孩子和双亲的关系),所以就能在内存中存储所有结点信息和所有的双亲关系
2.孩子链表表示法
存储所有结点的信息和所有孩子的关系。
1>头节点结构
data域是数据域,用来存储结点信息;
first域为指针域,用来存储结点孩子链表的入口地址,此链表记录了结点的所有孩子信息
2>表节点结构
childno域用来存储某个孩子结点的编号
next域为指针域,用来执行结点的下一个孩子所对应的表结点
3.左孩子右兄弟表示法
将结点最左边的孩子(如果存在的话)成为结点的左孩子,将位于结点右边且紧挨着它的兄弟(若存在的话)称为该结点的右兄弟
二叉树
递归树的方式
先序遍历:根左右
中序遍历:左根右
后序遍历:左右根
前序遍历中第一个数字总是根节点
线索二叉树
对一个二叉树线索化之后,可以得到某种遍历序列节点的前缀节点和后继节点。线索二叉树将一个二叉树变成了双向链表
哈夫曼树
在计算机数据处理中,霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现概率的方法得到的,出现概率高的字母使用较短的编码,反之出现概率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的
B-Tree
来源维基百科:
Rudolf Bayer 和 Ed McCreight 于1972年,在Boeing Research Labs 工作时发明了B 树,但是他们没有解释B 代表什么意义(如果有的话)。Douglas Comer 解释说: 两位作者从来都没解释过B树的原始意义。正如我们所见,“balanced”, “broad” 或 “bushy” 可能适合。其他人建议字母“B”代表 Boeing。源自于他的赞助,不过,看起来把B树当作“Bayer”树更合适些
阶为M的B-树的特点(来自数据结构与算法分析-C语言描述):
--|树的根或者是树叶节点,或者其儿子数在2和M之间
--|除根外,所有非树叶节点的儿子数在M/2和M之间
--|所有树叶都在相同深度上
所有数据都存放在树叶节点上,当插入一个关键字,只有在访问路径上的那些内部节点才有可能发生变化。
对于一个M阶的树,比较困难情况发生在接收该关键字的节点已经有了M个关键字的时候,这个关键字是的该节点具有M+1个关键字,我们可以把它分裂成两个节点。
他们分别具有「(M+1)/2向下和(M+1)/2」向上个关键字。由于这个父节点多了一个儿子,我们必须检查这个节点是否被父节点接收,如果父节点已经有M个儿子,那么这个父节点就要被分裂成两个节点。我们重复这个过程
直到找到一个父节点具有少于M个儿子,如果我们分裂根节点,那么我们就要创建一个新的根,这个根有两个儿子。
参考资料:
线索二叉树:http://www.cnblogs.com/GumpYa...
哈夫曼编码:https://zh.wikipedia.org/zh/%...
数据库索引数据结构:http://www.cnblogs.com/zcy_so...