查找
查找参数
ASL(Average Search Length),即平均查找长度,在查找运算中,由于所费时间在关键字的比较上,所以把平均需要和待查找值比较的关键字次数称为平均查找长度。
其中n为查找表中元素个数,Pi为查找第i个元素的概率,通常假设每个元素查找概率相同,Pi=1/n,Ci是找到第i个元素的比较次数。
线性表的查找
- 顺序查找
- 折半查找
- 分块查找
普通查找法
时间效率O(n)
折半查找法:
若k==R[mid].key,查找成功
若k<R[mid].key,则high=mid-1
若k>R[mid].key,则low=mid+1
练习:假定每个元素的查找概率相等,
判定树:总查找次数=每一层的元素个数*层数
总结:
时间复杂度O(log2n)
适合顺序存储结构的有序表,插入删除频繁时需要移动很多记录,更适用于静态查找表
分块查找(索引顺序查找)
分块有序,即分成若干子表,要求每个子表的数值都比后一块中数值小(但子表内部未必有序)然后将各子表中的最大关键字构成一个索引表,表中还要包含头指针(起始地址)
查找过程:对索引表使用折半查找法;确定子表之后在内部采用顺序查找法
过程:查找效率:ASL=Lb+Lw(对索引表查找的ASL+对块内查找的ASL)
O(log2(n/s+1)+s/2) 分成n/s块
适用于线性表需要快速查找又要动态变化时
树表
表结构在查找过程中动态生成,对于给定值key,若表中存在,则成功返回,若不存在,则插入生成新的记录。
排序二叉树
定义:要么是空树,或者满足以下条件
左子树所有的数<根节点<右子树所有的数
生成方法:【使用中序遍历】
查找方法:先判断根节点,再选择左右子树【时间复杂度O(log2n)】
BSTree Search(BSTree T,KeyType key)
{
if(!T||key->T->data)
return T;
else
{
if(T->data>key->data)Searth(T->left,key);
else Searth(T->right,key);
}
}
插入:若二叉排序树为空,则插入结点应为根节点;否则,继续在其左、右子树上查找。树中已有,不再插入;树中没有,查找直至某个叶子结点左右子树均为空时,插入【时间复杂度O(log2n)】
删除:
将因删除结点而断开的二叉链表重新链接起来,防止重新链接后树的高度增加
方法:删除的节点缺右子树用左子女填补;缺左子树用右子女填补;都不缺的在右子树上找中序遍历的第一个结点填补,再来处理这个结点的删除问题【时间复杂度O(log2n)】
查找的性能分析
最好:O(log2n)
最差:O((n+1)/2)
平衡二叉排序树(AVL树)
定义: 左右子树高度之差<=1,任意结点的平衡因子只能取-1,0,1【平衡因子算法:左子树深度-右子树深度】
(1)LL平衡旋转: 由于在A的左孩子B的左子树上插入结点F,使 A 的平衡因子由 1 增至 2 而失去平衡。故需进行一次顺时针旋转操作。 即将 A 的左孩子 B 向右上旋转代替 A 作为根结点, A 向右下旋转成为 B 的右子树的根结点。而原来 B 的右子树则变成 A 的左子树。
(2)RR平衡旋转: 由于在 A 的右孩子 C 的右子树上插入结点 F ,使 A 的平衡因子由 -1 减至 -2 而失去平衡。故需进行一次逆时针旋转操作。即将 A 的右孩子 C 向左上旋转代替 A 作为根结点, A 向左下旋转成为 C 的左子树的根结点。而原来 C 的左子树则变成 A 的右子树。
(3)LR平衡旋转: 由于在 A 的左孩子 B 的右子数上插入结点 F ,使 A 的平衡因子由 1 增至 2 而失去平衡。故需进行两次旋转操作(先逆时针,后顺时针)。即先将 A 结点的左孩子 B 的右子树的根结点 D 向左上旋转提升到 B 结点的位置,然后再把该 D 结点向右上旋转提升到 A 结点的位置。即先使之成为 LL 型,再按 LL 型处理
(4)RL平衡旋转: 由于在 A 的右孩子 C 的左子树上插入结点 F ,使 A 的平衡因子由 -1 减至 -2 而失去平衡。故需进行两次旋转操作(先顺时针,后逆时针),即先将 A 结点的右孩子 C 的左子树的根结点 D 向右上旋转提升到 C 结点的位置,然后再把该 D 结点向左上旋转提升到 A 结点的位置。即先使之成为 RR 型,再按 RR 型处理。
变种的AVL树--红黑树
哈希表
基本思想:记录的存储位置与关键字之间存在对应关系,LOC(i)=H(keyi)
优点:查找速度极快O(1),与元素个数没有关系
哈希方法(杂凑法):选取某个函数,依据该函数计算存储位置
冲突:不同的关键码映射到同一个哈希地址,key1!=key2,H(key1)=H(key2)
哈希函数的构造方法
直接定址法
例如:H[key]=a*key+b
优点:不会产生冲突
缺点:要占用连续地址空间,空间效率低
除留余数法
H[key]=key mod p
p选取小于元素个数的最大质数
数字分析法
折叠法
平方取中法
处理冲突的方法
(1)开放地址法
基本思想:有冲突时就去寻找下一个空的哈希地址,只要哈希表足够大,空的哈希地址总能找到
线性探测法:H=(Hash(key)+di) mod m,找下一个空余的位置【缺点:产生“二次聚集”现象,降低查找效率】
二次探测法:H=(Hash(key)+di) mod m,(d=1^2 ,2^2, -1^2, -2^2)
伪随机探测法:H=(Hash(key)+di) mod m(di为伪随机数)
【二次探测法和伪随机探测法可以避免“二次聚集”,但不能保证一定找到不发生冲突的地址】
步骤:
1.取数据的关键字key,计算其哈希函数值(地址),若该地址对应的存储空间为空,存入函数,否则执行第2步;
2.根据选择的冲突方法计算地址值存入下一个
(2)链地址法
基本思想:相同哈希地址的记录连成一单链表(同义词链表),m个哈希地址就设m个单链表,然后用一个数组储存表头
优点:非同义词没有冲突,无聚集现象;链表上结点空间动态申请,可扩长表长
有冲突处理的哈希表的查找
查找效率与装填因子a有关,不是o(1)也不是o(n)
a=表中填入的记录数/哈希表的长度
结论:链地址法优于开放地址法
地址的记录连成一单链表(同义词链表),m个哈希地址就设m个单链表,然后用一个数组储存表头
优点:非同义词没有冲突,无聚集现象;链表上结点空间动态申请,可扩长表长
有冲突处理的哈希表的查找
查找效率与装填因子a有关,不是o(1)也不是o(n)
a=表中填入的记录数/哈希表的长度
结论:链地址法优于开放地址法
除留余数法作哈希函数优于其他类型函数