目录
一、查找的基本概念
查找表:同一类型的数据元素构成的集合(元素之间是松散的关系,没有严格的前驱后继关系,应用灵活)
查找:根据给定的值,在查找表中确定一个关键字等于给定值的数据元素。
关键字:用来标识一个数据元素的某个数据项的值。唯一的标识一个记录的关键字是主关键字,用以识别若干记录的则为次关键字。
查找成功:给出整个记录的信息或指示该记录在查找表中的位置。查找不成功则给出空记录或者空指针。
对查找表的操作:查询(看有没有)、检索(获取信息)、插入、删除;仅作查询和检索为静态查找表,否则为动态查找表。
查找的方法取决于查找表的结构(以何种关系组织在一起),本章的出发点在于提高查找的效率。
提高效率: 在集合的数据元素之间人为加入加上某种确定的约束关系。
二、线性表的查找
1,顺序查找
应用范围:顺序表或线性表表示的静态查找表;表内元素之间无序。
以上算法每次都要判断i和key两个值会浪费时间,故采取下面的改进算法(设置监视哨)。在表头增加监视哨,将要查找的关键字放在0号位置,就可以不用判断i的值,因为表中找不到的情况下,会找到表头0号位置,i≥0.(在表长较长时,平均时间能少几乎一半)
循环执行次数:n-i+1,对其求和取平均(假设查找每个元素概率相等),时间效率:O((n+1)/2),空间复杂度O(1)一个辅助空间存放哨兵。
对于上图1:查找概率高的放后面,更快速查找(更少的比较次数)
2,折半查找(二分或对分查找)
表中数据有序时,用折半查找会很方便。
折半查找:每次将待查记录所在区间缩小一半。
先找中间位置mid=(low+high)/2并取整,将待查找的元素值和中间位置比较来确定往前找还是往后找,若往前,则high=mid-1;若往后,则low=mid+1;再找中间位置,重复以上步骤,直到mid==key;若出现high<low,则说明查找失败,结束。
以表中各元素的可能查找次数作为树的深度来构造判定树(且为圆形框代表表中存在该元素),则比较次数就是树的深度,可得比较次数不大于log2n+1。在叶子结点添加矩形框来表示表中没有出现的元素。平均查找长度比顺序表节省平均查找长度。
无序时无法折半,链式存储不方便找中间位置,需要遍历,还不直接顺序查找。
3,分块查找
也叫索引顺序表的查找,将表分为几块,且表有序或者分块有序(块间有序,块内无序)(块内有序更常用插入删除更方便)
三、树表的查找
当插入删除操作频繁时,改用动态查找表(表结构在查找过程中动态生成)维护表的有序性。
1,二叉排序树(二叉线索树、二叉查找树)
二叉排序树的性质:中序遍历获得递增有序序列。
查找递归算法:从根结点出发,判断下一查找左子树还是右子树
比较次数跟该结点在二叉排序树的层次有关,树的深度就是最坏时间复杂度。
平均时间复杂度跟树的形态有关,越平衡越好。
插入:
插入元素一定是在叶子结点上。
生成: 依次查找删除即可
删除:
- 删除叶子结点(直接删除)
- 删除的结点只有左子树或者只有右子树(用子树替换该结点)
将双亲结点的指针域改为“指向被删除结点的左子树或右子树” - 既有左子树又有右子树
要保持删除后仍然有序,因此删除某结点就用其前驱结点或者后继结点来代替该结点的位置。可以找到该结点a的左子树的根结点b的右子树的根结点c来代替该结点a,然后将c的子树替换c(这是用前驱结点,也就是左子树的最大结点)。或者直接用后继结点,也就是右子树的最小结点来代替。
2,平衡二叉树(AVL树)
平衡二叉树是任意左右子树高度差≤1的二叉排序树。平衡因子(BF)来表示任意结点左右子树高度差(从叶子结点到根结点都要满足平衡因子为-1,0,1),高度差保持在
O(log2n),查找长度就能保持在O(log2n)。
插入结点容易导致平衡二叉树失衡;找最小失衡子树,A是最小失衡树的根结点,C是新插入结点。对子树调整结构。
LL型:B带着左子树上升,A成为B的右孩子,B原来的右子树成为A的左孩子。
LR型:C结点单独上升,B称为C的左孩子,A成为C的右孩子,C的左孩子成为B的右子树,C的右孩子成为A的左子树。
RR型:B带着右子树上升,A成为B的左孩子,B原来的左子树成为A的右孩子。
RL型:C结点单独上升,A称为C的左孩子,B成为C的右孩子,C的左孩子成为A的右子树,C的右孩子成为B的左子树。
三、散列表的查找
1,基本概念
查找效率高,空间效率低。空间换时间。
按照以上方式构造的表就是散列表
冲突:不同的关键码映射到同一个散列地址。冲突不可避免,只能减少。
同义词:具有相同函数值的关键字。
2,散列函数的构造
构造的考虑因素:执行速度;散列表长度;关键字长度;关键字分布情况;查找频率。
构造的要求: 均匀存放,地址空间尽量小
构造散列表的方法:
- 直接定址法:优点是关键码的线性函数(一次函数)值为散列地址,不会产生冲突;缺点是占用连续地址空间,空间效率低。
- 除留余数法:关键码对某数p求余,余数作为散列地址
3,处理冲突方法
处理冲突的方法:
-
开放定址法:
解决冲突探测的方式:
(1)线性探测法:冲突的关键码存放时直接依次后移几格,直到找到一个非空的位置;找的时候也是同样的思路,只是可能会多比较几次。
(2)二次探测法:存储时,+1,-1,+4,-4,……依次试探。(这种探测方法要求散列表长度m为某个4K+3的质数 -
链地址法(拉链法)
链地址法处理冲突的优点:
非同义词:通过散列函数算出的散列地址不同就是非同义词,使用开放定址会导致非同义词冲突。
4,散列表的查找及性能分析
散列表比二分查找好。链地址法查找效率比开放地址法高,插入删除更方便。除留余数法中的除数取≤表长的质数。