顺序表查找
基本概念
给定一个值K,在含有n个结点的表中找出关键字等于给定值K的结点
查找表表示
(1)动态查找表和静态查找表
若在查找的同时对表做修改操作(如插入和删除),则相应的表称之为动态查找表。否则称之为静态查找表。
(2)内查找和外查找
若整个查找过程都在内存进行,则称之为内查找;反之,若查找过程中需要访问外存,则称之为外查找。
平均查找长度asl
查找运算的主要操作是关键字的比较,所以通常把查找过程中对关键字需要执行的平均比较次数(也称为平均查找长度)作为衡量一个查找算法效率优劣的标准。
其中:
① n是结点的个数;
② Ci是找到第i个结点所需进行的比较次数。
③Pi是查找第i个结点的概率.一般认为是等概率的,也就是1/n,顺序查找基本上也就是1/n*(1+2+…+n)=1/n*(n+1)*n/2=(n+1)/2
顺序表查找
数据结构定义
typedef struct {
KeyType key;
infoType data;
} NodeType;
typedef NodeType SeqList[n + 1]; //0号单元用作哨兵
一般顺序表的查找算法
int SeqSearch(SeqList R, KeyType k, int n)
{
//R[0]作为哨兵,用R[0].key==k作为循环下界的终结条件
R[0].key = k; //设置哨兵
i = n; //从后向前扫描
while (R[i].key != k)
i--;
return i; //返回其下标,若找不到,返回0
}
算法分析
- 查找成功:查找成功时的平均查找长度=(1+2+3+…+n)/n=(n+1)/2
- 查找失败时的查找长度=(n+1)
- 如果查找成功和不成功机会相等,顺序查找的平均查找长度为((n+1)/2+(n+1))/2= 3/4(n+1)
递增有序查找算法
int SeqSearchl(SeqList R, KeyType k, int n) //有序表的顺序查找算法
{
int i = n; //从后向前扫描,表按递增排序
while (R[i].key > k)
i--; //循环结束时,要么R[i].key=k,要么R[i].key<k
if (R[i].key == k)
return i; //找到,返回其下标
return 0; //没找到,返回O
}
算法分析
- 查找成功、失败
(n+1)/2 - 成功与失败机会均等,平均查找长度也为(n+1)/2
二分查找,必考
前提是有序表
思想
首先将待查的k值和有序表R[1…n]的中间位置mid上的记录(有序表的位置是从1开始,中间位置计算也是1+n /2)的关键字进行比较,若R[mid].key>k,则k在左子表R[1…mid-1]中(中间位置要往前移一位),接着再在左子表中进行二分查找即可。若R[mid].key<k,则说明待查记录在右子表R[mid+l…n](中间位置要往后移一位)中,接着只要在右子表中进行二分查找即可…二分查找的过程是递归的。
算法实现
递归实现
int BinSearch(SeqList R, KeyType k, int low, int high)
{
//在区间R[low...high]内进行二分递归,查找关键字值等于k的记录
//1ow的初始值为1,high的初始值为n
int mid;
if (low <= high) {
mid = (low + high) / 2;
if (R[mid].key == k) return mid; //查找成功,返回其下标
if (R[mid].key > k)
return BinSearch(R, k, low, mid - 1) ;//在左子表中继续查找
else
return BinSearch(R, k, mid + 1, high); //在右子表中继续查找
} else
return 0;
}
非递归算法
int BinSearch(SeqLiSt R, KeyType k, int n) //初始化上下界
{
int low = 1, mid, high = n;
while (low <= high) {
mid = (low + high) / 2;
if (R[mid].key == k)
return mid; //查找成功,返回其下标
if (R[mid].key > k)
high = mid - 1; //修改上界
else low = mid + 1; //修改下界
}
return 0; //查找失败,返回0值
}
算法分析
二分查找方法可以用一棵判定树描述,查找任一元素的过程对应该树中从根结点到相应结点的一条路径。
最短的查找长度为1,最长的查找长度为对应判定树的深度log2n+1,下取整.
平均查找长度为 (n+1)/n log2(n+1)-1≈log2(n+1)-1。
二分查找只适用于顺序结构上的有序表,对链式结构无法进行二分查找。
实例
利用二分查找算法在有序表R中插入一个元素x,并保持表的有序性。
算法实现
void BinInsert(SeqList R, KeyType x, InfoType y, int n)
{
int low = 1, high = n, mid, inspace, i ;
int find = 0; //find是作为是否找到与x相等的关键字,先假设未发现
while (low <= high && ! find) {
mid = (low + high) / 2;
if (x < R[mid].key) high = mid - 1;
else if (x > R[mid].key) low = mid + 1;
else find = 1;
}
if (find)
inspace = mid; //找到关键字与x相等,mid为x的插入位置
else
inspace = low; //所指向的结点关键字正好大于x,此时low即为插入位置
for (i = n; i >= inspace; i--)
R[i + 1] = R[i]; //后移结点,留出插入的空位
R[inspace].key = x; //插入结点,x是该结点的关键字,y是其他数据
R[inspace].data = y;
}
索引顺序查找
分块查找。它是一种性能介于顺序查找和二分查找之间的查找方法。
索引查找表的存储结构
块内无序,但块间有序
索引表
抽取各块中的最大关键字及其起始位置构成一个索引表
基本思想
(1)首先查找索引表
索引表是有序表,可采用二分查找或顺序查找,以确定待查的结点在哪一块。
(2)然后在已确定的块中进行顺序查找
由于块内无序,只能用顺序查找
查找长度
(1)查找索引表采用二分查找时的平均查找长度
ASLblk=log2(b+1)-1+(s+1)/2≈log2(n/s+1)+s/2
(2)查找索引表采用顺序查找时的平均查找长度
ASLblk=(b+1)/2+(s+1)/2=(b+s)/2+1=(n/s+s)/2+1 =(块数+块长)/2+1
当s取 根号n(即s=b)时, ASLblk 达到最小值 根号n+1,即当采用顺序查找确定块时,应将各块中的结点数选定为 根号n。
三种顺序查找比较
- 顺序查找
优点:算法简单,对表的存储结构无任何要求。
缺点:查找效率低,查找成功的平均查找长度为(n+1)/2,查找失败的查找长度为(n+1)。 - 二分查找
优点:二分查找的速度快,效率高,查找成功的平均查找长度约为log2(n+1)-l。
缺点:要求表以顺序存储表示并且是按关键字有序,使用高效率的排序方法也要花费O(nlog2n)的时间。另外,当对表结点进行插入或删除时,需要移动大量的元素,所以二分查找适用于表不易变动且又经常查找的情况。 - 分块查找,索引
优点:在表中插入或删除一个记录时,只要找到该记录所属的块,就可以在该块内进行插入或删除运算。因为块内记录是无序的,所以插入或删除比较容易,无需移动大量记录。
缺点:是需要增加一个辅助数组的存储空间和将初始表块排序的运算,它也不适宜用链式存储结构。
此外,顺序查找、二分查找和分块查找三种查找算法的时间复杂度分别为:O(n)、O(log2n)和O(根号n )。