首先,先明白查找表这个概念。
查找表:用来查找的数据集合称为查找表,它由同一类型的数据元素(或记录)组成,可以是一个数组或链表等数据类型。对查找表经常进行的操作一般有4种:
静态查找表:一个查找表的操作只涉及上述操作1和2,无须动态修改查找表,此类查找表称为静态查找表。
动态查找表:需要动态地插入或删除的查找表称为动态查找表。
适合静态查找表的查找方法有顺序查找、折半查找、散列查找。
适合动态查找表的查找方法有二叉排序树的查找、散列查找。
顺序查找又称线性查找,它对顺序表和链表都适用。
顺序查找是最简单的一种查找方法,基本思想为从线性表的一段开始,逐个检查关键字是否满足给个的条件。为了很多不必要的判断语句,通常在算法中引入“哨兵”。
int Search_Seq(SSTable S,ElemType key){
S.elem[0] = key;
// 哨兵
int i;
for(i=S.length;S.elem[i]!=key;--i);
return i;
}

对于n个元素的表,给定值key与表中第i个元素相等,则定位到第i个元素时,需要比较i次(参考书上写n-i+1)关键字的比较。查找成功时,顺序查找的平均长度为
P1*1+P2*2+...+Pi*i(i从1到n)
或P1*(n-1+1)+P2*(n-2+1)+...+Pi*(n-i+1)(i从1到n)
其中Pi=1/n(i从1到n)
ASL成功 = (n+1)/2
时间复杂度为O(n)
折半查找又称为二分查找,它适用于有序的顺序表。
int Binary_Search(SSTable S,ElemType key){
int low = 0,high = S.length-1,mid;
while(low<=high){
mid = (low+high)/2;
if(S.elem[mid] == key)
return mid;
else if(S.elem[mid] > key)
high = mid - 1;
else
low = mid + 1;
}
return -1;
}
运行结果:

【注】:数组下标从0开始。
折半查找的执行过程为:
7 10 13 16 19 29 32 33 37 41 43
low = 0,high = 10
mid = 10/2 = 5 对应29 < 32,low = 5+1 = 6
32 33 37 41 43
low = 6,high = 10
mid = 16/2 = 8 对应37 > 32,high = 8-1 = 7
32 33
low = 6,high = 7
mid = 13/2 = 6 对应32 = 32 返回6
折半查找的过程可以用下图所示的二叉树来描述(来自参考书)。

用折半查找法查找给定值的比较次数最多不会查过树的高度。其查找成功的平均查找长度为
ASL = (11+22+…+h*2^(h-1))/n = [(n+1)/n]*log 2 (n+1)
折半查找的时间复杂度为O(log 2 n)
散列表是根据关键字而直接进行访问的数据结果。散列表建立了关键字和存储地址之间的一种直接映射关系。
1.开放地址法,指可存放新表项的空闲地址既向它的同义词表项开放,也向它的非同义词开放。
数学递推公式为Hi=(H(key)+di)%m
H(key)为散列函数,m表示散列表的长度,di为递增序列。
取某一递增序列,对应的处理方法就是确定的。
当di=0^2 ,1*2 ,(-1)^2 ,...,(-k)^2,称为平方探测法,
其中k<=m/2(m为散列表的长度,且m必须可以表示成4k+3的素数)
又称二次探测法,可以避免”堆积“问题,缺点是不能探测散列表的所有单元,但至少可以探测到一半单元。
2 拉链法(链接法)
适用于经常进行插入和删除的情况。
对于不同的关键字可能通过散列函数映射到同一地址,为了避免非同义词产生冲突,可以把所有同义词存储在一个线性链表中,这个线性链表由其散列地址唯一标识。
如 关键字序列为 13 15 17 22 9 7 4,散列函数H(key)=key%7,用拉链法解决冲突可以得到如下