学不完的数据结构(五)查找

725 阅读7分钟

6.查找

6.1 基本概念

​ 平均查找长度:一次查找的长度是指需要比较的次数,平均查找长度则是所有查找过程中进行关键字的比较次数的平均值。

ASL=\sum ^{n}_{i=1}P_iC_i

​ 其中,n是查找表的长度,Pi是查找第i个数据元素的概率,Ci是找到第i个数据元素所需进行的比较次数。

6.2 顺序查找和折半查找

顺序查找:

C_i=i\qquad {成功}\\
ASL_{成功}=\sum ^{n}_{i=1}P_i(i)\\
P_i=\frac {1}{n}时,有ASL_{成功}=\frac{n+1}{2}\\
\qquad \\
C_i=n+1\qquad {失败}\\
ASL_{失败}=n+1

​ 对线性的链表只能顺序查找

有序表的顺序查找:

查找成功平均查找长度一般线性表的顺序查找一样。

ASL_{成功}=\frac {n+1}{2}\\
ASL_{失败}=\sum _{j=1}^{n}q_j(l_j-1)=\frac{1+2+3+...+n+n}{n+1}=\frac{n}{2}+\frac{n}{n+1}

折半查找:

​ 仅适用于有序的顺序表,且只适合顺序存储结构,不适合链式。

查找成功时查找长度为从根结点目的结点路径上的结点数

查找失败时查找长度为从根结点到对应失败结点的父结点路径上的结点数

查找失败时比较次数最少\lceil log_2(n+1) \rceil-1,次数最多\lceil log_2(n+1) \rceil

​ 每个判定树存在n+1个查找失败结点(对应空链域)。

​ 折半查找的速度不一定快于顺序查找。

​ 查找成功平均查找长度log_2(n+1)-1,(当给出具体数值时画出判定树计算,不要信公式!)

​ 折半查找判定树高度\lceil log_2(n+1) \rceil

​ 判断方法:1.为平衡二叉树

​ 2.任何非叶子结点的(左子树数量-右子树数量同号或\ge 0或\le 0

​ 折半查找关键字比较序列的判定同二叉排序树前面小于x的递增,大于x的递减(一般x为最后一个)。

​ 算法:

​ 非递归方式

int B_Search(int L[],int key,int n)
{
    int low=0,high=n-1,mid;
    while(mid<=high)
    {
        mid=(low+high)/2;
        if(L[mid]==key)
        	return mid;
        else if(L[mid]>key)
       		high=mid-1;
        else
        	low=mid+1;
    }
    return -1;
}

​ 递归方式

int B_S(int L[],int key,int low,int high)
//初始时low为0,high为n-1
{
    int mid=(low+high)/2;
    if(L[mid]==key)
        return mid;
    else
        return (L[mid]>key)?B_S(L,key,low,mid-1):B_S(L,key,mid+1,high);
}

分块查找

​ 将序列分作b块,每块里面有n/b个元素。

块内元素是无序的,块之间有序的。

​ 在块间使用顺序查找

ASL_{成功}=L_I+L_S=\frac{b+1}{2}+\frac{\frac{n}{b}+1}{2}=\frac{b}{2}+\frac{n}{2b}+1
\\(ASL)'=0时,b=\sqrt {n} 
\\此时取最小

​ 在块间使用折半查找

ASL_{成功}=L_I+L_S=\lceil log_2(b+1)\rceil +\frac{1}{2}+\frac{n}{2b}
平均查找长度 成功ASL_{成功} 失败ASL_{失败}
顺序查找 \frac{n+1}{2} n+1
有序表顺序查找 \frac{n+1}{2} \frac{n}{2}+\frac{n}{n+1}
二分查找 log_2(n+1)-1
分块查找 视情况

6.3 B树

6.3.1 B树性质

B树性质(m阶):

​ 1.所有非叶结点子树数量\ge 2(根结点为终端节点时为1)

​ 2.除根节点之外的非叶子结点子树数量\in[\lceil m/2\rceil ,m]

​ 3.同一节点关键字非递减有序

​ 4.叶子节点都出现在同一层次(最低,不计入高度)

​ 5.叶结点数量为关键字数+1

子树数量范围 关键字数量范围
根节点 ,[2,m] [1,m-1]
除根结点外的非叶子结点 [\lceil m/2\rceil,m] [\lceil m/2\rceil-1,m-1]

6.3.2 B树特例

​ B树中特例一:(m阶,高度h,结点数n,关键字数k)

​ 结点数n:1+2+2\lceil m/2 \rceil+...+2\lceil m/2 \rceil ^{h-2}=2{\frac{\lceil m/2 \rceil ^{h-1}-1}{\lceil m/2 \rceil -1}+1}

​ 关键字k:2(\lceil m/2 \rceil)^{h-1}-1 (通过计算叶子节点-1=k)

​ 结点与关键字关系:n=\frac{k-1}{\lceil m/2 \rceil -1}+1 (除根节点外每个节点有m/2-1个子节点)

​ B树中特例二:(m阶,高度h,结点数n,关键字数k)

​ 结点数n:1+m+m^2+...+m^{h-1}=\frac{m^h-1}{m-1}

​ 关键字k:m^h-1 (每个结点有m-1个关键字)

​ 结点与关键字关系:k=n(m-1)

m阶B树(高h,关键字数k,结点数n) 最少 最多
知h求k 2(\lceil m/2 \rceil)^{h-1}-1 m^h-1
知h求n 2\frac{\lceil m/2 \rceil ^{h-1}-1}{\lceil m/2 \rceil -1}+1 \frac{m^h-1}{m-1}
知k求h log_m(n+1) log_{\lceil m/2 \rceil }[\frac{n+1}{2}]+1
知k求n \frac {k}{m-1} \frac{k-1}{\lceil m/2 \rceil -1}+1
知n求h log_m[n(m-1)+1] log_{\lceil m/2 \rceil}[(\frac{n-1}{2})(\lceil m/2 \rceil -1)+1]+1
知n求k (n-1)(\lceil m/2 \rceil -1)+1 n(m-1)

6.3.3 B树操作

​ B树查找:先在有序表中进行查找,若找到则查找成功,否则按照对应指针信息到所指的子树中查找。

​ B树插入:(一定是插入在最底层中的某个非叶子结点内)

​ 插入后结点关键字个数\lt m:直接插入

​ 插入后结点关键字个数\ge m分裂结点,以中间位置为界,同时将中间位置(\lceil m/2 \rceil结点插入到该结点父结点,若导致父结点关键字个数超出上限,则继续分裂递归进行,可能会使得高度增加。

​ B树删除

​ 删除的关键字不在终端结点

​ 1):小于k的子树关键字个数\gt\lceil m/2 \rceil-1,则找到k前驱k',用k'取代k,再递归删除k'

​ 2):大于k的子树关键字个数\gt\lceil m/2 \rceil-1,则找到k后驱k',用k'取代k,再递归删除k'

​ 3):两个子树的关键字个数都为\lceil m/2 \rceil -1,则直接将两个子结点合并,直接删除k

​ 删除的关键字在终端结点

​ 1):直接删除关键字:若所在结点关键字个数\gt \lceil m/2 \rceil -1,则直接删除

​ 2):兄弟够借:若所在结点关键字个数=\lceil m/2 \rceil -1,且相邻兄弟结点关键字个数\ge \lceil m/2 \rceil,调整该结点,借到父结点相邻的关键字填入,同时借兄弟结点中关键字填入父结点。(先试左后试右

​ 3):兄弟不够借:若所在结点关键字个数=\lceil m/2 \rceil -1,且相邻兄弟结点关键字个数都为\lceil m/2 \rceil -1,则将关键字删除与右(左)兄弟结点双亲结点中关键字进行合并,并且递归的进行。双亲结点中关键字个数会减少,若双亲结点是根结点并且关键字个数减少至0,则直接将根结点删除,合并后新结点成为根。

6.3.4 B+树基本概念

​ B+树性质:

​ 1):每个分支结点最多有m棵子树

​ 2):非叶根结点至少有两棵子树,其他每个分支结点至少有\lceil m/2 \rceil棵子树

​ 3):结点的子树个数=关键字个数

​ 4):所有叶结点包含全部关键字,并且叶结点中的关键字有序,并且相邻叶子结点之间按大小顺序关系通过指针链接起来。

​ 5):所有分支结点中仅包含它的各个子结点(即下一级的索引块)中关键字的最大值及指向其子结点的指针。

​ B+树与B树的差异:(B+树支持顺序查找) ​ 1):B树中每个结点关键字为子树数量-1,B+树中每个结点关键字数为子树数量。

​ 2):B+树中每个结点(非根内部结点)关键字范围是[m/2]<=n<=m(根结点: 1<=n<=m),在B树中,每个结点(非根内部结点)关键字个数n的范围是[m/2]-1<=n<=m-1(根结点: 1<=n<=m-1)。

​ 3):在B+树中,叶结点包含信息,所有非叶结点仅起到索引作用,非叶结点中的每个索引项只含有对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。

​ 4):在B+树中,叶结点包含了全部关键字,即在非叶结点中出现的关键字也会出现在叶结点中;而在B树中,叶结点包含的关键字和其他结点包含的关键字是不重复的。

6.4 散列表

6.4.1 散列表基础概念

散列函数定义域必须包含所有关键字

​ 1):直接定址法

为常数H(key)=a*key+b(a,b为常数)

​ 2):除留余数法

为常数H(key)=key\%p(p为常数)

处理冲突方法:

​ 1:开放定址法

H_i=(H(key)+d_i)\%m

​ 不能随便删除物理中的元素,一般做一个删除标记。

​ 1):线性探测法

d_i=0,1,2,3,...

​ 冲突发生时,则查看下一个单元,直到找出一个空闲单元或者查遍全表。

​ 会导致大量元素“聚集”,从而降低查找效率。

​ 2):平方探测法(二次探测法)

d_i=0^2,1^2,-1^2,2^2,-2^2...k^2,-k^2(k<m/2)

​ 不能探测到散列表所有单元,但是至少一半

​ 3):再散列法

H_i=(H(key)+i*Hash_2(key))\%m

​ 2:拉链法(链接法)

​ 将所有的同义词储存到一个线性链表中,适用于经常插入删除的情况。

6.4.2散列查找性能

​ 查找效率取决于三个因素:散列函数处理冲突的方法装填因子

装填因子:表示一个表的装满程度

表中记录数散列表长度a={表中记录数n}/{散列表长度m}

​ 散列表的平均查找长度依赖于装填因子

​ 散列表的平均查找长度:(n个记录,表长m,冲突次数表为A,散列表地址从0到m-1)

ASL_{成功}=\frac{1}{n} \sum_{i=0}^{n-1}(A[i]+1)\\
ASL_{失败}=\frac{1}{m} \sum_{i=0}^{m-1}(往后的第一个空域到当前位置的长度)