(一)顺序查找
typedef *** KeyType; //关键字类型 typedef struct { KeyType key; //查询关键字字段 ...; }ElemType; //记录类型 typedef struct { ElemType *elem; //首地址,0号他用 int length; //表长 }SSTable;
int Search_Seq(SStable ST, KeyType x){ //思路:从前向后逐一比较,找到返回位序, 否则返回0 //思路:添加哨兵,从后向前比较,省略每次循环时的越界检查 ST.elem[0].key = x; for( i = ST.length; ST.elem[ i ].key != x; --i ) ; return i; return 0; }//O(n)
(二)折半查找
思路:每轮与有序区间的中间元素进行比较,若相等则返回当前元素的位置;若待查找元素大于中间元素,则到右侧区间查找;若查找元素小于中间元素,则到左侧区间查找,不断重复前述折半查找过程,直至查找成功,或者待查找区间失效! 折半查找判定树任意节点左右子树深度相差最多为1,可证此类树深度为[log2n]+1,可证查找成功时最坏查找长度为[log2n]+1,复杂度O(log2n)
使用折半查找的前提是:1.有序 2.可以随机访问
(三)分块查找
分块查找是折半查找和顺序查找的一种改进方法,分块查找由于只要求索引表是有序的,对块内节点没有排序要求,因此特别适合于节点动态变化的情况。分块查找要求把一个大的线性表分解成若干块,每块中的节点可以任意存放,但块与块之间必须排序。假设是按关键码值非递减的,那么这种块与块之间必须满足已排序要求,实际上就是对于任意的i,第i块中的所有节点的关键码值都必须小于第i+1块中的所有节点的关键码值。此外,还要建立一个索引表,把每块中的最大关键码值作为索引表的关键码值,按块的顺序存放到一个辅助数组中,显然这个辅助数组是按关键码值费递减排序的。查找时,首先在索引表中进行查找,确定要找的节点所在的块。由于索引表是排序的,因此,对索引表的查找可以采用顺序查找或折半查找;然后,在相应的块中采用顺序查找,即可找到对应的节点。
(四)二叉排序树查找(二叉搜索树)
若它的左子树不空,则左子树上所有结点的值均小于它根结点的值。 若它的右子树不空,则右子树上所有结点的值均大于它根结点的值。 它的左、右树又分为⼆叉排序树 显然,二叉排序树与二叉树一样,也是通过递归的形式定义的。因此,它的操作也都是基于递归的方式。
性能 二叉搜索树一般不直接使用,因为有可能性能很差,二叉搜索树的查找效率主要与树的高度有关,节点个数相同的情况下,二叉搜索树最优的情况是完全二叉树,高度最小,其时间复杂度为O(logN),最差情况下退化为单支树,树的高度等于节点个数,时间复杂度为O(N)。
(五)平衡二插树查找
平衡二叉树的概念:任意节点的平衡因子(左右子树深度之差)的绝对值不超过1。
平衡二叉搜索树的构造:先按照二叉搜索树的节点插入方法插入节点,不满足平衡要求时调整!找到最小不平衡二叉树的根后,根据不平衡种类作相应的旋转!既要保证平衡,也要保证是二叉搜索树!
平衡二叉树的平衡调整:
LL型,RR型,LR型,RL型
(六)B-树和B+树查找
B-树: 概念: “平衡”的“多路”“查找”树( 平衡二叉树的推广) 特点:平衡(叶子节点/失败节点同层) ,多关键字,查找树
一颗m阶的B树是空树,或是满足下列特性的m叉树: (1)树中每个结点至多有m棵子树(m-1个关键字) (2) 根结点或者为叶子结点或者至少有两棵子树 (3)根之外所有非终端结点至少有ceil(m/2)棵子树 (4) 非终端结点包含信息(n, A0,K1,A1,K1,A2,...,Kn ,An) Ki为关键字,有序; Ai为指向子树根结点的指针,且Ai所指子树中所有结点关键字均介于Ki-1和Ki之间.(n<m) (5)叶结点出现在同一层上,不带信息,相当于查找失败结点
查找: 从根结点出发,沿树形结构搜索结点和在结点内进行顺序(或折半)查找 两个过程交叉进行。
插入: 若查找成功则不能插入,否则,应试图插入到查找路径中最后一个内部结点上。内部结点关键字个数须在Ceil(m/2)-1和m-1之间,若超界则“分裂” 分裂:选中间关键字上移,当前结点分成两个,下层相应分;若上移导致双亲关键字个数超界则继续上移.
删除: 被删节点为最下层非叶节点则直接删,否则用右侧子树最小记录顶替(规约) 内部节点关键字数量有下界Ceil(m/2)-1,低于此值要“求助”或“合并”
2-3树是最简单的B-树
B+树: 概念: “平衡”的“多路”“索引”树 特点:平衡(叶子节点/记录节点同层), 多关键字, 索引树, 顺序与随机查找并行
(七)哈希查找
哈希表:根据设定的哈希函数和处理冲突的方法,将一组关键字映象到一组有限的连续的存储空间上,以关键字对应的Hash函数值作存储地址,如此所得的查找表表称为哈希表。
散列:映像过程称为哈希造表或散列,存储地址称为哈希地址或散列地址。
查找性能:依赖于Hash函数与冲突处理方法的质量!
哈希函数:
线性函数:H(key) = a % key + b
除留取余法:H(key) = key % P( P通常取素数,或至少不包含20 以下的质因子)
数字分析法:关键字由 s 位数字组成 (u1, u2, …, us),分析全体关键字,从中提取区分度好(数字分布均匀)的若干位构成Hash地址,位数取决于拟定的表长,可直接用某几位区分度好的数字,或者叠加求和 ,适用场合:知道关键字的全部情况,能预先估计出各位上数字出现的情况,选取时尽量避免冲突,且要避免空间浪费
平方取中法:以关键字的平方值的中间几位作为存储地址。平方值的中间各位能受到整个关键字中各位的影响,区分度通常较好,如2061^2=4310541, 2161^2=4734741,H(2061)=310,适用场合:事先不知道关键字情况,或者关键字各位上都有某些数字重复多次出现.较常用
折叠法:将关键字分割成位数相同的若干部分(最后一部分可不同),然后取它们的叠加和为哈希地址。 移位叠加:低位对齐相加,后舍弃越界位 间界叠加:来回折叠取值,后对其相加,如ISBN号0-442-20586-4,每4位一块 移位叠加:5864+4220+04=10088,H(key)=0088 间界叠,适用场合:关键字位数特别多,且每一位上数字分布均匀加:5864+0224+04=6092,H(key)=6092
随机数法:H(key) = Random(key),random()为伪随机函数,适用场合:此方法用于对长度不等的关键字构造哈希函数。
字符串哈希函数:一串字符串里面的各个字符所对应的Unicode编码,进行∑(Ui*3L-i)。
开放定址法:H0 = H(key);Hi = ( H0+ di ) MOD L 第i次产生冲突则在哈希值的基础上加增量di ,如此重复直到Hs处无冲突。L为表长。增量序列: {1,2,3,…,m-1} 线性探测再散列 {1的2,-1的2,2的2,-2的2,3的2,…,±k的2次方}二次探测再散列 伪随机数序列 伪随机探测再散列 线性探测再散列 ( H(key)+[1|2|3|…] )%L 二次探测再散列 ( H(key)+[1的2|-1的2|2的2|-2的2|…] )%L
冲突处理方法
链地址法:冲突的记录构成一个链表,哈希表存其头指针
开放地址法
在散列表(Hash Table)中,负载因子α是当前元素数量与散列表大小的的比值。尽管增加散列表的大小可以减少冲突的可能性,但这并不意味着α>1时就不会发生碰撞。实际上,即使α>1,仍然可能存在碰撞。负载因子只是用来衡量散列表的装载程度,而不是用来避免碰撞的决定性因素。但是若装填因子α为1,则向散列表中散列元素时一定会产生冲突。