1.查找的基本概念
查找 :在具有相同类型的记录构成的集合中找出满足给定条件的记录。
查找的结果 :若在查找集合中找到了与给定值相匹配的记录,则称查找成功;否则,称查找失败。
静态查找 :不涉及插入和删除操作的查找 。
动态查找 :涉及插入和删除操作的查找。
静态查找适用于:查找集合一经生成,便只对其进行查找,而不进行插入和删除操作; 或经过一段时间的查找之后,集中地进行插入和删除等修改操作;
动态查找适用于:查找与插入和删除操作在同一个阶段进行,例如当查找成功时,要删除查找到的记录,当查找不成功时,要插入被查找的记录。
查找结构 :面向查找操作的数据结构 ,即查找基于的数据结构。
线性表:适用于静态查找,主要采用顺序查找技术、折半查找技术。
树表:适用于动态查找,主要采用二叉排序树的查找技术。
散列表:静态查找和动态查找均适用,主要采用散列技术。
平均查找长度:将查找算法进行的关键码的比较次数的数学期望值定义为平均查找长度。计算公式为:
其中:n:问题规模,查找集合中的记录个数;
pi:查找第i个记录的概率;
ci:查找第i个记录所需的关键码的比较次数。
结论:ci取决于算法;pi与算法无关,取决于具体应用。如果pi是已知的,则平均查找长度只是问题规模的函数。
2.顺序查找 (线性查找)
基本思想:
从线性表的一端向另一端逐个将关键码与给定值进行比较,
若相等,则查找成功,给出该记录在表中的位置;
若整个表检测完仍未找到与给定值相等的关键码,则查找失败,给出失败信息。
改进的顺序查找
基本思想:设置“哨兵”。
哨兵就是待查值,
将哨兵放在查找方向的尽头处,
免去了在查找过程中每一次比较后都要判断查找位置是否越界,从而提高查找速度。
顺序查找查找性能的改进方法
记录每个数据的访问频率,
把访问频率高的数据移向顺序表的右端
可以减少查找成功时所进行的比较次数,提高效率
构造有序的顺序表
减少查找失败时所进行的比较次数,提高查找效率
顺序查找的优点:
算法简单而且使用面广。
顺序查找的缺点:
平均查找长度较大,特别是当待查找集合中元素较多时,查找效率较低。
3.折半查找
适用条件:
基本思想:
在有序表中(low, high,low<=high),
取中间记录作为比较对象,
若给定值与中间记录的关键码相等,则查找成功;
若给定值小于中间记录的关键码,则在中间记录的左半区继续查找;
若给定值大于中间记录的关键码,则在中间记录的右半区继续查找。
不断重复上述过程,直到查找成功,或所查找的区域无记录,查找失败。
int BinSearch1(int r[ ], int n, int k){
int low=1; high=n,mid;
while (low<=high)
{
mid=(low+high)/2;
if (k<r[mid]) high=mid-1;
else if (k>r[mid]) low=mid+1;
else return mid;
}
return 0;
}
int BinSearch2(int r[ ], int low, int high, int k){
if (low>high) return 0;
else {
mid=(low+high)/2;
if (k<r[mid])
return BinSearch2(r, low, mid-1, k);
else if (k>r[mid])
return BinSearch2(r, mid+1, high, k);
else return mid;
}
}
判定树:折半查找的过程可以用二叉树来描述,
树中的每个结点对应有序表中的一个记录,
结点的值为该记录在表中的位置。
通常称这个描述折半查找过程的二叉树为折半查找判定树,简称判定树。
⑴ 当n=0时,折半查找判定树为空;
⑵ 当n>0时,
折半查找判定树的根结点为mid=(n+1)/2,
根结点的左子树是与有序表r[1] ~ r[mid-1]相对应的折半查找判定树,
根结点的右子树是与r[mid+1] ~ r[n]相对应的折半查找判定树。
判定树的特点
任意两棵折半查找判定树,若它们的结点个数相同,则它们的结构完全相同
判定树的性质
任意结点的左右子树中结点个数最多相差1
任意结点的左右子树的高度最多相差1
任意两个叶子所处的层次最多相差1
查找成功时的平均查找长度ASL:
4.树表的查找技术
二叉排序树(Binary Search Tree)
二叉排序树(也称二叉查找树):或者是一棵空的二叉树,或者是具有下列性质的二叉树:
⑴若它的左子树不空,则左子树上所有结点的值均小于根结点的值;
⑵若它的右子树不空,则右子树上所有结点的值均大于根结点的值;
⑶ 它的左右子树也都是二叉排序树。
二叉排序树的插入
void InsertBST(BiNode<int> * & root , BiNode<int> *s);
分析:若二叉排序树为空树,则新插入的结点为新的根结点;否则,新插入的结点必为一个新的叶子结点,其插入位置由查找过程得到。
若二叉排序树为空树,则新插入的结点为新的根结点;
否则,如果插入的值比根节点值大,则在右子树中进行插入;否则,在左子树中进行插入。
递归。
二叉排序树的构造
从空的二叉排序树开始,依次插入一个个结点 。
BiSortTree::BiSortTree(int r[ ], int n){
for (i=0; i<n; i++) {
s=new BiNode<int>;
s->data=r[i];
s->lchild=s->rchild=NULL;
InsertBST(root, s);
}
}
二叉排序树的删除
在二叉排序树上删除某个结点之后,仍然保持二叉排序树的特性。
分三种情况讨论:
2. 若结点p只有左子树, 则只需重接p的左子树;
若结点p只有右子树, 则只需重接p的右子树;
3. 若结点p的左右子树均不空,则
3.1 查找结点p的右子树上的最左下结点s及s双亲结点par;
3.2 将结点s数据域替换到被删结点p的数据域;
3.3 若结点p的右孩子无左子树,
则将s的右子树接到par的右子树上;
二叉排序树的查找
在二叉排序树中查找给定值k的过程是:
⑴ 若root是空树,则查找失败;
⑵ 若k=root->data,则查找成功;否则
⑶ 若k<root->data,则在root的左子树上查找;否则
⑷ 在root的右子树上查找。
上述过程一直持续到k被找到或者待查找的子树为空,如果待查找的子树为空,则查找失败。
二叉排序树的查找效率在于只需查找二个子树之一。
-
5.平衡二叉树(AVL树)
平衡二叉树:或者是一棵空的二叉排序树,或者是具有下列性质的二叉排序树:
⑴ 根结点的左子树和右子树的深度最多相差1;
⑵ 根结点的左子树和右子树也都是平衡二叉树。
平衡因子:结点的平衡因子是该结点的左子树的深度与右子树的深度之差。
结点的平衡因子=HL-HR
在平衡树中,结点的平衡因子可以是1,0,-1。
最小不平衡子树:在平衡二叉树的构造过程中,以距离插入结点最近的、且平衡因子的绝对值大于1的结点为根的子树。
基本思想:
在构造二叉排序树的过程中,每插入一个结点时,首先检查是否因插入而破坏了树的平衡性,
若是,则找出最小不平衡子树,
在保持二叉排序树特性的前提下,调整最小不平衡子树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。
设结点A为最小不平衡子树的根结点,对该子树进行平衡调整归纳起来有以下四种情况:
1. LL型
2. RR型
3. LR型
4. RL型