静态查找表
查找的定义:给定一个值k,在含有n个结点的表(或文件)中找出关键字等于给定值k的结点,若找到,则查找成功,输出该结点在表中的位置;否则查找失败,输出查找失败的信息。
特点:仅查找,而不改变查找表中的数据元素(如高考成绩表、本单位职工信息表等);
一:顺序查找
查找过程:对给定的一关键字 K,从线性表(顺序表或线性链表均可以)的一端开始,逐个进行记录的关键字和 K 的比较,直到找到关键字等于 K 的记录或到达表的另一端。通常在线性表的0号单元存储key值以作为“哨兵”。
监视哨作用:避免每步都去判断是否越界。
- 优点:算法简单,无需排序,采用顺序和链式存储均可。
- 缺点:平均查找长度较大。
- 平均查找长度:
n:表中记录个数
Pi:查找第i个记录的概率
Ci:找到第i个记录需要进行的对比次数
#include <iostream>
using namespace std;
/**
* @param args
*/
//在顺序表ST中顺序查找其关键字等于key的数据元素,若找到,则函数值为该元素在表中的位置,否则返回0
static int searchSeq(int st[], int key,int len){//st表的0号单元不使用
//从后往前找,并在0号单元设置"监视哨"
//int len = sizeof(st)/sizeof(st[0])-1;//这样是错误的,得到的结果永远是1
st[0] = key;//设置监视哨(哨兵)
while(st[len] != key){
len--;
}
return len;
}
int main(void){
int st[] = {0, 10, 20, 40, 80, 30, 60, 25};//0号单元不使用 ;
int len = sizeof(st)/sizeof(st[0])-1;//数组占内存总空间,除以单个元素占内存空间大小,
cout<<"数组长度为:"<<len<<endl;
int key = 0;
cin>>key;
int result = searchSeq(st, key,len);
if(result > 0)
cout<<"查找"<<key<<"成功,在查找表中的位置是"<<result<<endl;
else
cout<<"查找"<<key<<"失败"<<endl;
system("pause");
return 0;
}
二:折半查找(有序表的查找)
前提条件:必须在具有顺序存储结构的有序表中进行。
查找思想:先确定待查找记录所在的范围,然后逐步缩小范围,直到找到或确认找不到该记录为止。
#include <iostream>
using namespace std;
//在有序表st中折半查找其关键字等于key的数据元素,若找到,则函数值为该元素在表中的位置,否则返回0
static int searchSeq(int st[], int key,int len){//st表的0号单元不使用
//int len = sizeof(st)/sizeof(st[0])-1;//这样是错误的,得到的结果永远是1
int low = 1, high = len-1;
while(low <= high){
int mid = (low + high)/2; //取中间值
if(st[mid] == key){
return mid;
}else if(st[mid] > key)
high = mid - 1;
else
//st[mid] < key
low = mid + 1;
}
return 0;
}
int main(void){
//有序表的二分查找测试
int st[] = {0, 8, 17, 25, 44, 68, 77, 98, 100, 115, 125};//0号单元不使用
int len = sizeof(st)/sizeof(st[0]);//数组占内存总空间,除以单个元素占内存空间大小,
cout<<"数组长度为:"<<len<<endl;
int key = 0;
cin>>key;
int result = searchSeq(st, key,len);
if(result > 0)
cout<<"查找"<<key<<"成功,在查找表中的位置是"<<result<<endl;
else
cout<<"查找"<<key<<"失败"<<endl;
system("pause");
return 0;
}
算法分析:
为了分析二分查找的性能,可以用二叉树来描述二分查找过程。把当前查找区间的中点作为根结点,左子区间和右子区间分别作为根的左子树和右子树,左子区间和右子区间再按类似的方法,由此得到的二叉树称为二分查找的判定树。
例:关键字序列{8,17,25,34,48,57,68}的判定树
由于第 k 层结点的查找次数各为 k 次(根结点为第1层),而第 k 层结点数最多为 2^k-1 个。假设该二叉树深度为 h,则等概率下,二分查找的成功的 ASL 为:
上式中: j×2j−1 的意思是:第j层 × 当前层的节点数 2j−1
最坏情形下:① 取等号;② 最大的结点数n =
2h
-1;
则h=
log2(n+1)
,因此 ,
ASL=n+1nlog2(n+1)−1
推导过程如下:
ASL=2ASL−ASL=1n[(1×21+2×22+…+h×2h)−(1×20+2×21…h×2h−1)]
=
1n[2h×h−(20+21+22+…+2h−1)]=1n[2h×h−(2h−1)]
=
1n[(n+1)(log2(n+1)−1)+1]
=
n+1nlog2(n+1)−1
当 n 很大时,ASL –>
log2(n+1)−1
,则其时间复杂度为O(
log2n
)。
二分查找的效率比顺序查找高,但只能适用于有序表,而排序本身是一种很费时的运算,即使采用高效率的排序方法也要花费O(
nlog2n
)的时间;另外,二分查找只适用于顺序存储结构(对线性表表示无法进行二分查找)
三:索引顺序查找(分块查找)
查找思想:是顺序查找的一种改进方法,就是把被查找的表分成若干块,块中记录的存放顺序是无序的,但块与块之间必须按关键字有序。即第一块中任一记录的关键字都小于第二块中任一记录的关键字,而第二块中任一记录的关键字都小于第三块中任一记录的关键字,依此类推。
该方法要为被查找的表建立一个索引表,索引表中的一项对应于表中的一块,索引表中含有这一块中的最大关键字和指向块内第一个记录位置的指针,索引表中各项关键字有序。
查找步骤:
- 折半查找索引表(当然也可以顺序查找索引表,整体的查找速度也是比原始的顺序查找高的,但远不及折半查找),确定要找的记录所在块;
- 在相应的块中顺序查找。
算法分析:
设把长度为n的表分成均等的b个子表,每个子表有S个对象,则 b=
ns
。又设表中每个对象的查找概率相等,子表为
1b
,子表内各对象为
1s
。
则顺序查找确定块的成功ASL为:ASL1=
b+12+s+12
=
b+s2+1
折半查找确定块的成功ASL为: ASL2=
log2(b+1)−1+s+12
=
log2(1+ns)+s2
可见,索引查找的ASL与表的长度n和子表内对象的个数s有关。