查找
动态查找:查找的同时对表做修改工作
静态查找:查找的同时不涉及表的修改操作
内查找:在内存中查找
外查找:需访问外存
ASL -- 平均查找长度
ASL成功 -- 成功查找到查找表中元素平均需要的关键字比较次数
ASL失败 -- 没有查找到查找表中元素平均需要的关键字比较次数
ASL是衡量查找算法性能好坏的指标,ASL越大,时间性能越差
从表的一端开始,顺序扫描线性表,依次将扫描到的关键字和给定值k相比较:
一、顺序查找
两种情况的ASL
ASL不成功=n
顺序查找
int SeqSearch(RecType R[], int n, KeyType k)
{
int i = 0;
while (i < n && R[i].key != k)//从表头往后找
{
i++;
}
if (i >= n)
return 0; //未找到返回
else
return i + 1;//找到返回逻辑序号+1
}
哨兵
可以在R 的末尾增加一个关键字为k的元素,称之为哨兵。 增加哨兵后查找过程不再需要判断i是否超界,从而提高查找速度。 带哨兵的顺序查找算法如下:
//改进算法 -- 哨兵
int SeqSearch(RecType R[], int n, KeyType k)
{
int i = 0;
R[n].key = k;
while (R[i].key!=k)
i++;
if (i == n)
return 0;
else
return i + 1;
}
二、折半查找
折半查找
折半查找也称为二分查找,要求线性表中的元素必须己按关键字值有序(递增或递减)排列。
int BinSearch(RecType R[], int n, KeyType k)
{
int low = 0, high = n - 1, mid;
while (low <= high) //当前区间存在元素时循环
{
mid = (low + high) / 2;
if (k == R[mid].key) //查找成功返回其逻辑序号mid+1
return mid + 1;
if (k < R[mid].key) //继续在R[low..mid-1]中查找
high = mid - 1;
else
low = mid + 1; //继续在R[mid+1..high]中查找
}
return 0;
}
判定树
当前查找区间的中间位置上的元素作为根;
左子表和右子表中的元素分别作为根的左子树和右子树。
这样的二叉树称为判定树或比较树。
外部结点
外部结点即查找失败对应的结点,是虚拟的
n个关键字:内部结点为n个,外部结点为n + 1个
ASL的两种情况
1.成功二分查找
//恰好是走了一条从判定树的根到被查元素的路径,经历比较的关键字次数恰为该元素在树中的层次。
2.失败二分查找
//比较过程经历了一条从判定树根到某个外部结点的路径,所需的关键字比较次数是该路径上内部结点的总数,即该外部结点的层次减1。
折半查找改进
假设递增有序顺序表为R[0..n - 1],其中可能存在多个相同关键字的元素,现在求第一个大于等于k的元素位置(从0开始的下标),如果k大于R中全部元素,则返回结果n。
例如,n = 7,R[0..6] = { 1,2,4,4,4,5,6 },k = 2时返回1,k = 4时返回2,k = 10时返回7。
采用基本折半查找算法,对于上述R,当k = 4时返回3,显然是错误的。
int BinSearch2(RecType R[], int n, KeyType k)
{
int low = 0, high = n - 1, mid;
while (low <= high) //查找区间有一个或者更多元素时
{
mid = (low + high) / 2;
if (k <= R[mid].key)
high = mid - 1; //继续在左区间查找
else
low = mid + 1; //继续在右区间查找
}
return high + 1;
}
三、索引存储结构和分块查找
索引存储结构 = 主数据表 + 索引表
索引表中的每一项称为索引项,索引项的一般形式是:(关键字,地址) 关键字唯一标识一个元素,地址作为指向该关键字对应元素的指针,也可以是相对地址。
typedef struct
{ KeyType key; //KeyType为关键字类型
int link; //指向对应块的起始下标
} IdxType; //索引表元素类型
int IdxSearch(IdxType I[],int b,RecType R[],int n,KeyType k)
{ int s=(n+b-1)/b; //s为每块的元素个数,即s=n/b
int low=0,high=b-1,mid,i;
while (low<=high) //折半查找,找到的位置为high+1
{ mid=(low+high)/2;
if (I[mid].key>=k)
high=mid-1;
else
low=mid+1;
}
//在主数据表的high+1块中进行顺序查找
i=I[high+1].link;
while (i<=I[high+1].link+s-1 && R[i].key!=k)
i++;
if (i<=I[high+1].link+s-1)
return i+1; //查找成功,返回该元素的逻辑序号
else
return 0; //查找失败,返回0
}
若以折半查找来确定元素所在的块,则成功时的平均查找长度为:
若以顺序查找来确定元素所在的块,则成功时的平均查找长度为: