数据结构——查找
查找的基本概念
1.查找表
查找表是由一组记录构成的表或文件。每个记录由若干数据项组成,并假设有唯一标识该记录的 关键字(类似数据)。
2.内查找与外查找
整个查找过程都在内存(数组或链表),为内查找;若需要访问外存,为外查找。
3.动态查找表与静态查找表
查找的同时可对表做修改(如删除),则相应的表称之为动态查找表;否则称之为静态查找表。
4.查方法的性能指标
查找运算时间主要花费在关键字比较上,通常,把查找过程中执行的关键字平均比较个数(称为平均查找长度)作为衡量一个查找算法效率优劣的标准。
平均查找长度ASL(Average Search Length)定义为:
成功情况下的平均查找长度——ASL成功
不成功情况(失败)下的平均查找长度——ASL不成功
线性表的查找
1.顺序查找
思路:从表(顺序或链式)的一端开始,顺序扫描线性表,依次将扫描到的关键字和给定值k相比较,
若 扫描到的 关键字=k,则查找成功;
若 扫描结束未找到 关键字=k 的记录,则查找失败。
ASL成功 =(n+1)/2
ASL不成功=n
2.二分查找(折半查找)
思路:二分查找 要求线性表的记录必须按 关键字值有序(递增或递减)排列。用下图利用判定树解释:
在等概率假设下,二分查找成功时的平均查找长度为:ASL成功≈log2(n+1)-1
在等概率假设下,二分查找不成功时的平均查找长度为:ASL不成功=log2(n+1)
3.分块查找
索引存储结构:索引存储结构 = 主数据表 + 索引表
借用下图例子解释索引存储结构:
思路:数据整体是无序,但是分块后按块是有序的。
在索引表(有序)中按关键字查找:可以 顺序查找 或 二分查找。
在对应的数据块(无序)中查找:只能顺序查找块中元素。
算法的时间复杂度:折半查找确定块,顺序查找确定块中元素,ASL是俩次查找平均查找长度之和。
树表的查找
以 二叉树或其他树 作为查找表的形式,称为树表。树表主要采用链式存储结构,是动态查找表。
1.二叉排序树(BST)
BST又称为二叉查找(搜索)树。
- 左子树上所有结点值(关键字值)小于根结点值。
- 右子树上所有结点值均大于根结点值。
- 二叉排序树中没有相同关键字的结点
在树中插入关键字为k的新结点,插入后仍满足BST性质
(1)若二叉排序树T为空,则创建一个key域为k的结点,将它作为根结点;
(2)否则将k和根结点的关键字比较,若两者相等,则说明树中已有此关键字k,无须插入,直接返回0;
(3)若k更小,将k插入根结点的左子树中。
(4)否则将它插入右子树中。
n个不同关键字构成的不同的二叉排序树数量如下:
2.平衡二叉树
若一棵二叉树中每个结点的左、右子树的高度至多相差1,则称此二叉树为平衡二叉树(AVL)。
平衡因子:该结点左子树的高度减去右子树的高度。
在插入操作中若破坏了平衡则需要调整:LL调整,RR调整,RL调整,LR调整
(其实就是看左,右树的高度进行相应调整,逻辑很简单),如下图。
在平衡二叉树上进行查找的过程和在二叉排序树上进行查找的过程完全相同
含有n个结点的平衡二叉树的平均查找长度为O(log2n)。
3.B树
B树又称为多路平衡查找树,是一种组织和维护外存文件系统非常有效的数据结构。在数据库索引与操作系统中有重要应用。
一棵m阶B树或者是一棵空树,或者是满足要求的m叉树
- m阶代表一个树节点最多有多少个查找路径,m=m路,当m=2则是2叉树,m=3则是3叉
- 树中每个结点至多有m个孩子结点(即至多有m-1个关键字)
- 除根结点外,其他非叶子结点至少有ceil(m/2)(对其上取整)个孩子结点
- 若根结点不是叶子结点,则根结点至少有两个孩子结点
- 所有节点关键字是按递增次序排列,并遵循左小右大原则
- 所有外部结点都在同一层上。B树是所有结点的平衡因子均等于0的多路查找树
- 外部结点就是失败结点,指向它的指针为空,不含有任何信息,是虚设的。一棵B树中总有n个关键字,则外部结点个数为n+1。
B树和二叉树相比本质未变,每个结点数据变多,能充分利用磁盘存储空间。
查找方式:由于B树本来就有顺序,可以从上到下,从左至右比较,类比二分查找法。
插入和删除:操作涉及到父,子结点的拆分合并,必须满足B树结构条件。
如插入23和29:
4.B+树
相对于B树来说B+树更充分的利用了节点的空间,让查询速度更加稳定,其速度完全接近于二分法查找。
- B+跟B树不同,B+树的非叶子节点不保存关键字记录的指针,只进行数据索引,这样使得B+树每个非叶子节点所能保存的关键字大大增加
- B+树叶子节点保存了父节点的所有关键字记录的指针,所有数据地址必须要到叶子节点才能获取到。所以每次数据查询的次数都一样
- B+树叶子节点的关键字从小到大有序排列,左边结尾数据都会保存右边节点开始数据的指针。
- 非叶子节点的子节点数=关键字数
哈希表的查找
1.哈希表的基本概念
散列表(Hash table,也叫哈希表),是根据键(Key)而 直接 访问在内存存储位置的数据结构。它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数(哈希函数),存放记录的数组称做散列表(哈希表)。
散列函数的规则是:通过某种转换关系,使关键字适度的分散到指定大小的的顺序结构中,越分散,则以后查找的时间复杂度越小,空间复杂度越高。因此,哈希函数是用空间换取时间效率的算法。
2.哈希函数构造方法
①直接定址法
直接定址法是以关键字k本身或关键字加上某个数值常量c作为哈希地址的方法。
直接定址法的哈希函数h(k)为:
h(k) = k+c
如: h(学号) = 学号-201001001
②除留余数法
哈希函数h(k)为:
h(k)=k mod p
③数字分析法
3.哈希冲突的解决方法
哈希冲突是指哈希函数算出来的地址被别的元素占用了。
哈希表装填因子定义为:α= 填入表中的元素个数 / 哈希表的长度
①开放地址法
冲突时找一个新的空闲的哈希地址。那么,如何找空闲单元呢?
- 线性探测法
d0=h(k)
di=(di-1+1) mod m (1≤i≤m-1)
示例:在电影院中找被占用位置的后面空位置 - 平方探测法
-d0=h(k)
di=(d0±i2) mod m (1≤i≤m-1)
查找的位置依次为:d0、 d0+1、 d0-1 、 d0+4、 d0-4…
平方探测法是一种较好的处理冲突的方法,可以避免出现堆积现象。它的缺点是不能探测到哈希表上的所有单元,但至少能探测到一半单元。
②拉链法
拉链法是把所有的同义词用单链表链接起来的方法。(也是更为常见的哈希结构)