B 树
m阶B树:是满足下列特性的树:
(1) 树中每个结点至多有m棵子树;
(2) 若根结点不是终端结点,则至少有两棵子树;
(3) 除根结点外,其他非终端结点至少有 m/2(向上取整) 棵子树;
(4)所有非终端结点都包含以下数据:
(n,A0,K1,A1,K2,…,Kn,An)
其中,n(m/2(向上取整)- 1≤n≤m 1)为关键码的个数;
Ki(1≤i≤n)为关键码,且Ki<Ki+1(1≤i≤n-1);
Ai(0≤i≤n)为指向子树根结点的指针,且指针Ai所指子树中所有结点的关键码均小于Ki+1大于Ki。
(5)所有叶子结点都在同一层上,B树是高平衡的。
B+树
B+树是B树的变体,也是一种多路搜索树:
m阶B+树的结构定义如下:
(1)每个结点至多有m个子结点;
(2)每个结点(除根外)至少有ceiling(m/2)个子结点;
(3)根结点至少有两个子结点;
(4)有k个子结点的结点必有k个关键码。
m阶B+树:是满足下列特性的树:
⑴ 含有m个关键码,每一个关键码对应一棵子树。
⑵ 关键码Ki是它所对应的子树的根结点中的最大(或最小)关键码。
⑶ 所有终端结点中包含了全部关键码信息,以及指向关键码记录的指针。
⑷ 所有终端结点按关键码的大小链在一起,形成单链表,并设置头指针。
散列表(hash)的查找技术
散列函数的构造
直接定址法
除留余数法
数字分析法
平方取中法
折叠法(分段叠加法)
冲突处理方法
开放定址法
链地址法
建立公共溢出区
散列的基本思想:在记录的存储地址和它的关键码之间建立一个确定的对应关系。
这样,不经过比较,一次读取就能得到所查元素的查找方法。
散列表:采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间
称为散列表。
散列函数:将关键码映射为散列表中适当存储位置的函数。
散列地址:由散列函数所得的存储位置址 。
散列既是一种查找技术,也是一种存储技术。
散列查找的限制
散列技术一般不适用于允许多个记录有同样关键码的情况。
有冲突,降低了查找效率,体现不出计算式查找的优点
散列方法也不适用于范围查找
不能查找最大值、最小值
也不可能找到在某一范围内的记录。
散列技术的关键问题:
⑴ 散列函数的设计。如何设计一个简单、均匀、存储利用率高的散列函数。
⑵ 冲突的处理。如何采取合适的处理冲突方法来解决冲突。
直接定址法
散列函数是关键码的线性函数,即:
H(key) = a * key + b (a,b为常数)
除留余数法
散列函数为:H(key)=key mod p
一般情况下,选p为小于或等于表长(最好接近表长)的最小素数。
适用情况:
除留余数法是一种最简单、也是最常用的构造散列函数的方法,并且不要求事先知道关键码的分布。
数字分析法
根据关键码在各个位上的分布情况,选取分布比较均匀的若干位组成散列地址。
适用情况:
(1)事先知道关键码的分布
(2)关键码的分布均匀
平方取中法
对关键码平方后,按散列表大小,取中间的若干位作为散列地址(平方后截取。
例:散列地址为2位,则关键码1234的散列地址为:(1234)²=1522756
适用情况:
事先不知道关键码的分布且关键码的位数不是很大。
冲突的处理
开放定址法
由关键码得到的散列地址一旦产生了冲突,就去寻找下一个空的散列地址,并将记
录存入。
用开放定址法处理冲突得到的散列表叫闭散列表。
线性探测法
当发生冲突时,从冲突位置的下一个位置起,依次寻找空的散列地址。
对于键值key,设H(key)=d,闭散列表的长度为m,则发生冲突时,寻找下一个散列
地址的公式为:
Hi=(H(key)+di) % m (di=1,2,…,m-1)
堆积:在处理冲突的过程中出现的非同义词之间对同一个散列地址争夺的现象。
在线性探测法构造的散列表中查找算法——伪代码
假设给定的值为K,根据所设定的散列函数h,计算出散列地址h (K)
否则将该地址中的值与K比较,若相等则检索成功,算法结束
否则,按建表时设定的处理冲突方法查找探查序列的下一个地址,如此反复下去
直到某个地址空间未被占用(查找不成功,可以插入),算法结束
或者关键码比较相等(有重复记录,不需要插入)为止,算法结束
如果探测完整个hash表,都没有进行插入或查找失败,则抛出空间异常(hash表容
量不足)
int HashSearch1(int ht[ ], int m, int k)
{
j=H(k);
if (ht[j]==k) return j; //没有发生冲突,比较一次查找成功
i=(j+1) % m;
while (ht[i]!=Empty && i!=j)
{
if (ht[i]==k) return i; //发生冲突,比较若干次查找成功
i=(i+1) % m; //向后探测一个位置
}
if (i==j) throw "溢出";
else ht[i]=k; //查找不成功时插入
}
拉链法(链地址法)
基本思想:将所有散列地址相同的记录,即所有同义词的记录存储在一个单链表中(称为同义词子表),在散列表中存储的是所有同义词子表的头指针。
用拉链法处理冲突构造的散列表叫做开散列表。
设n个记录存储在长度为m的散列表中,则同义词子表的平均长度为n / m。
在拉链法构造的散列表查找算法——伪代码
- 计算散列地址j;
- 在第j个同义词子表中顺序查找;
- 若查找成功,则返回结点的地址;
否则,将待查记录插在第j个同义词子表的表头。
Node<int> *HashSearch2(Node<int> *ht[ ], int m, int k)
{
j=H(k);
p=ht[j];
while (p && p->data!=k)
p=p->next;
if (p->data= =k) return p;
else {
q=new Node<int>; q->data=k;
q->next= ht[j];
ht[j]=q;
}
}
散列查找的性能分析
由于冲突的存在,产生冲突后的查找仍然是给定值与关键码进行比较的过程。
在查找过程中,关键码的比较次数取决于产生冲突的概率。而影响冲突产生的因素有:
(1)散列函数是否均匀
(2)处理冲突的方法
(3)散列表的装载因子
α=表中填入的记录数/表的长度