数据结构(C语言版)---查找
1、查找表:同一类型的数据元素构成的集合。
2、对查找表进行的操作:查询某特定元素、检索满足条件的元素的属性、插入元素、删除元素。
1)若对查找表进行的操作只涉及前两种,则为静态查找表;需要进行插入和删除,则为动态查找表。
2)适合静态查找表的查找方法:顺序查找、折半查找、散列查找。
3)适合动态查找表的查找方法:二叉排序树(二叉平衡树和B树都是二叉排序树的改进)的查找、散列查找。
3、顺序查找(线性查找):用于在线性表中进行查找。
1)一般线性查找表的顺序查找
(1)对于n个元素的表,查找成功的平均查找长度为ASL成功=Pi(n-i-+1),(从1到N求和)。
(2)当每个元素查找概率相等时,即Pi=1/n,ASL成功=(n+1)/2,ASL失败=n+1。
2)有序表的顺序查找
查找成功的平均查找长度和一般线性查找表的顺序查找一样;查找不成功时ASL失败=n/2+n/(n+1)。
3)一般线性表的存储结构描述
typedef struct {
int * elem;//基地址
int tablelen;//表的长度
}SSTable;
4)在顺序表ST中顺序查找关键字为key的元素
int Seqsearch(SSTable ST, int key)
{
ST.elem[0] = key;
int i;
for ( i = ST.tablelen; ST.elem[i] != key; –i);
return i;
}
5)顺序查找表,找到后和其前面的元素交换
int Seqsrch(SSTable r, int k)
{
int i = 0, temp;
while ((r.elem[i]!=k)&&(i<r.tablelen))
{
i++;
}
if (i < r.tablelen&&i>0)
{
temp = r.elem[i];
r.elem[i] = r.elem[i – 1];
r.elem[i – 1] = temp;
return –i;
}
else
{
return -1;
}
}
4、折半查找(二分查找):仅适用于有序的顺序表。
1)折半查找的过程用二叉树描述,称为判定树。
(1)若有序序列有n个元素,则对应的判定树有n个圆形的非叶结点和n+1个方形叶结点。
(2)当元素个数为n时,树的高度为h=log2(n+1)。
2)查找成功的平均查找长度:ASL成功=log2(n+1)-1,折半查找的时间复杂度为O(log2n)。
3)折半查找的存储结构必须具有随即存取的特性。
4)折半查找—在有序表L中查找关键字为key的元素
int binarysearch(SSTable L, int key)
{
int low = 0, high = L.tablelen – 1, mid;
while (low <= high)
{
mid = (low + high) / 2;
if (L.elem[mid] == key)
{
return mid;
}
else if (L.elem[mid] > key)
{
high = mid – 1;
}
else
{
low = mid + 1;
}
}
return -1;
}
5、有序表的顺序查找和折半查找的区别:有序表的顺序查找中的线性表可以为链式存储结构。
6、分块查找(索引顺序查找)
1)操作:将查找表分成若干块,块内无序,块间有序。
2)将长度为n的查找表均匀的分为b块,每块有s个记录,在等概率情况下:
(1)若在块内和索引表中均采用顺序查找,则平均查找长度为ASL=(b+1)/2+(s+1)/2=(s2+2s+n)/2s;
(2)若s=√n,则平均查找长度取最小值√n+1。
(3)若对索引表采用折半查找,则平均查找长度为ASL=log2(b+1)+(s+1)/2。
7、B树:所有结点的平衡因子均等于0的多路平衡查找树。
1)B树的阶:B树中所有结点的孩子结点数的最大值。
2)m阶B树的性质:
(1)每个结点至多有m棵子树,即至多含有n-1个关键字;
(2)若根结点不是终端节点,则至少有两棵子树;
(3)除根结点外的所有非叶结点至少有m/2棵子树,即至少含有m/2-1个关键字;
(4)非叶结点的结构
n(结点中关键字的个数) | p0 | k1 | p1 | k2 | p2 | … | kn | pn |
其中,ki为结点的关键字,且k1<k2<…<kn,pi为指向子树根结点的指针,且pi-1所指子树中所有结点的关键字均小于ki,pi所指子树中所有结点的关键字均大于ki;
(5)所有叶结点都出现在同一层次上。
3)B树的高度
若n>=1,则对于任意一棵包含n个关键字、高度为h、阶数为m的B树
(1)h>=logm(n+1);
(2)若让每个结点中的关键字个数达到最少,则容纳同样多关键字的B树的高度达到最大。
4)B树的查找
基本操作:在B树中找结点,在结点内找关键字。
5)B树的插入
(1)定位:找出插入该关键字的最底层中的某个非叶结点。(B树中的插入关键字一定插入在最底层中的某个非叶结点内)
(2)插入:每个非失败结点的关键字个时速都在区间[m/2-1,m-1]内,
插入后的结点关键字个数小于m,可以直接插入;插入后的结点关键字个数大于m-1,必须对结点进行分裂。
6)B树的删除
(1)删除非终端结点时
若小于k的子树中关键字个数>m/2-1,则找出k的前驱值,并用k的前驱值取代k,再递归删除前驱。
若大于k的子树中关键字个数>m/2-1,则找出k的后继值,并用k的后继值取代k,再递归删除后继。
若前后两个子树中关键字个数均为m/2-1,则直接将两个子节点合并,直接删除k。
(2)删除终端结点时
直接删除关键字。若被删除关键字所在结点的关键字个数>m/2-1,直接删除。
兄弟够借。若被删除关键字所在节点删除前的关键字个数=m/2-1,且与此结点相邻的右(左)兄弟结点的关键字个数>=m/2-1,则调整该结点的兄弟结点及双亲结点,达到平衡。
兄弟不够借。若被删除关键字所在节点删除前的关键字个数=m/2-1,且与此结点相邻的右(左)兄弟结点的关键字个数=m/2-1,则将该结点的兄弟结点及双亲结点中的关键字进行合并。
8、B+树
1)m阶B+树的性质:
(1)每个分支结点最多有m棵子树;
(2)非叶根结点至少有两棵子树,其他每个分支结点至少有m/2棵子树;
(3)结点的子树个数与关键字个数相等;
(4)所有叶结点包含全部关键字及指向相应记录的指针,叶结点中将关键字按大小顺序排列,并且相邻叶结点按大小顺序互相链接起来;
(5)所有分支节点中仅包含它的各个子结点中关键字的最大值及指向其子结点的指针。
8、B树和B+树的区别
1)B+树中,n个关键字的结点只含有n棵子树,即每个关键字对应一颗子树;B树中,n个关键字的结点含有n+1棵子树。
2)B+树中,每个结点的关键字个数n的范围m/2<=n<=m,根结点1<=n<=m;B树中,每个结点的关键字个数n的范围m/2-1<=n<=m-1,根结点1<=n<=m-1。
3)B+树中,叶结点包含信息,非叶结点仅起索引作用,非叶结点中的每个索引项只含有对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。
4)B+树中,叶结点包含了全部关键字,即在非叶结点中出现的关键字也会出现在叶结点中;B树中,叶结点包含的关键字和其他结点包含的关键字是不重复的。
9、散列表:根据关键字而直接进行访问的数据结构。
散列函数:一个把查找表中的关键字映射陈该关键字对应的地址的函数,记作Hash(key)=Addr。
冲突:散列函数将两个或两个以上的不同关键字映射到同一个地址。
散列表建立了关键字和存储地址之间的一种直接映射关系。
10、常用的散列函数
1)直接定址法
直接取关键字的某个线性函数值为散列地址,散列函数为H(key)=a*key+b,ab为常数。
适合关键字的分布基本连续的情况,不会产生冲突。
2)除留余数法
假定散列表的表长为m,取一个不大于m但最接近或等于m的质数p,散列函数为H(key)=key%p。
3)数字分析法
4)平方取中法
5)折叠法
11、处理冲突的方法
假定选定散列函数H(key),Hi表示发生冲突后第i次探测的散列地址。
1)开放定址法:指可存放新表项的空闲地址既向它的同义词表项开放,又向它的非同义词表项开放。Ni=(H(key)+di)%m,(m表示散列表表长,di为增量序列)。
当某一增量序列确定后,对应处理方法确定,通常有4种:线性探测法、平方探测法、再散列法、伪随机序列法。
2)拉链法(链接法)
12、散列表的查找效率取决于三个因素:散列函数、处理冲突的方法、装填因子。
装填因子:一个表的装满程度。α=表中记录数n/散列表长度m。α,发生冲突的可能性越大。