数据结构——查找

news2024/11/22 14:35:17

文章目录

    • **1 查找的基本概念**
    • **2 顺序查找和折半查找**
      • **2.1 顺序查找**
      • **2.2 折半查找**
      • **2.3 分块查找**
    • **3 树型查找**
      • **3.1 二叉排序树BST**
        • **3.1.1 二叉排序树的定义**
        • **3.1.2 二叉排序树的查找**
        • **3.1.3 二叉排序树的插入**
        • **3.1.4 二叉排序树的构造**
        • **3.1.5 二叉排序树的删除**
        • **3.1.6 二叉排序树的查找效率分析**
      • **3.2 平衡二叉树**
        • **3.2.1 平衡二叉树的定义**
        • **3.2.2 平衡二叉树的插入**
        • **3.2.3 平衡二叉树的删除**
        • **3.2.4 平衡二叉树的查找**
      • **3.3 红黑树**
        • **3.3.1 红黑树的定义**
        • **3.3.2 红黑树的插入**
        • **3.3.3 *红黑树的删除**
    • **4 B树和B+树**
      • **4.1 B树及其基本操作**
        • **4.1.1 B树的高度(磁盘存取次数)**
        • **4.1.2 B树的查找**
        • **4.1.3 B树的插入**
        • **4.1.4 B树的删除**
      • **4.2 B+树的基本概念**
    • **5 散列表(哈希表)**
      • **5.1 基本概念**
      • **5.2 散列函数的构造方法**
      • **5.3 处理冲突的方法**
        • **5.3.1 开放定址法**
        • **5.3.2 拉链法(链接法,chaining)**
      • **5.4 散列查找及性能分析**

1 查找的基本概念

(1)查找

在数据集合中寻找满足条件的数据元素的过程。

(2)查找表(查找结构)

用于查找的数据集合称为查找表。

(3)静态查找表

无法动态的插入或者删除数据,就是静态查找表;于此对应的就是动态查找表

静态:顺序查找,折半查找,散列查找

动态:二叉排序树的查找,散列查找

(4)关键字

数据元素中唯一标识该元素的某个数据项的值,基于关键字的查找,结果都是唯一的

(5)平均查找长度

所有查找过程中进行关键字的比较次数的平均值,即ASL= ∑ i = 1 n P   i   C   i   \sum\limits_{i=1}^n{P~i~C~i~}\quad i=1nP i C i 

n是表长度,Pi是查找第i个数据元素的概率(一般相等),即Pi=1/n;Ci是找到第i个数据元素所需进行的比较次数

2 顺序查找和折半查找

2.1 顺序查找

(1)一般线性表的顺序查找

typedef struct
{
    Elemtype *elem;
    int tablelen;
}Sstable;

int Search_seq(Sstable ST,Elemtype key)
{
    ST.elem[0]=key;                                     \\哨兵位
    for(int i = ST.tablelen; ST.elem[i]!=key; i--)      \\从后往前找
    {
        return i;
    }
}

引入哨兵可以使得循环不必判断数组是否会越界,在多个算法中都适用

查找概率都为1/n,定位第i个元素时需要进行n-i+1次对比,故

ASL成功=(n+1)/2

ASL不成功=n+1

当n较大,平均查找长度也大,效率低,优点是对元素的存储没有要求,顺序存储和链式存储都可以

(2)有序表的顺序查找

查找之前就知道表是有序的,则查找失败就不用再比较表的另一端的数据,直接返回失败信息,降低了ASL

可以用判定树来描述有序线性表的查找过程

在这里插入图片描述

有序表的查找成功ASL和一般表的一样

但是查找失败的 ASL不成功= n 2 \dfrac{n}{2} 2n+ n n + 1 \dfrac{n}{n+1} n+1n

2.2 折半查找

又称二分查找,仅适用于有序的顺序表

给定key值与表的中间元素对比,相等就成功;如果不成功,如果key大于中间值,则左指针右移到刚刚的中间位置,如果key小于中间值,则右指针左移到刚刚的中间位置

如此重复,直到找到或者查找失败,即左指针在右指针的右边

int Binarysearch(Sstable L,Elemtype 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;
}

折半查找的过程可以用一棵判定树来描述

在这里插入图片描述

折半查找的比较次数最火不会超过树的高度

ASL成功=log2(n+1)-1

时间复杂度:O(log2n)

折半查找仅适合顺序存储区结构,不适合链式存储结构,同时还要有序排列

2.3 分块查找

又称索引顺序查找,吸取了顺序查找和折半查找各自的优点,既有动态结构,又适于快速查找

基本思想:把查找表分为若干子块,块内元素可以无序,但是块间元素时有序的;再建立一个索引表,索引表的每个元素含有各块的最大关键字和各块中的第一个元素的地址,索引表按关键字有序排列

查找过程:在索引表中确定key所在的块,可以顺序也可以折半,然后在块内顺序查找

如下图:

在这里插入图片描述

设索引查找和块内查找的平均长度为Li和Ls

则ASL=Li+Ls

将长度为n的表分为b块,每块s个记录,等概率的情况下,均采用顺序查找,则ASL= b + 1 2 \dfrac{b+1}{2} 2b+1+ s + 1 2 \dfrac{s+1}{2} 2s+1= s 2 + 2 s + n 2 s \dfrac{s^2+2s+n}{2s} 2ss2+2s+n

3 树型查找

3.1 二叉排序树BST

3.1.1 二叉排序树的定义

若左子树非空,则左子树上所有结点的值均小于根结点的值;若右子树非空,则右子树上所有结点的值均大于根结点的值;左右子树也是一棵二叉排序树==(左小右大)==

对二叉排序树进行中序遍历就得到一个递增的有序序列

3.1.2 二叉排序树的查找

树非空,给定值key先于根结点比较,如果大于根结点,就进入右子树查找,小于就进入左子树查找,不断重复,直到找到或失败,是递归的过程,递归算法的执行效率低

非递归算法:

BSTnode* BSTsearch(Bitree T,Elemtype key)
{
    while(T!=NULL && key!=T->data)
    {
        if(key<T->data)
        {
            T=T->lchild;
        }
        else
        {
            T=T->rchild;
        }
    }
    return T;
}

3.1.3 二叉排序树的插入

二叉排序树是一种动态树表,树的结构不是一次性生成,是在查找的过程不存在关键字时进行插入

树为空直接插入,key大于根结点插入右子树,否则插入左子树,一定是个叶结点

在这里插入图片描述

int BSTinsert(Bitree &T, keytype k)
{
    if(T==NULL)
    {
        T=(Bitree)malloc(sizeof(BSTnode));
        T->data=k;
        T->lchild=T->rchild=NULL;
        return 1;
    }
    else if(k==T->data)
    {
        return 0;
    }
    else if(k<T->data)
    {
        return BSTinsert(T->lchild,k);
    }
    else
    {
        return BSTinsert(T->rchild,k);
    }
}

3.1.4 二叉排序树的构造

从空树出发,依次输入元素,再插入

void CreatBST(Bitree &T, Keytype str[],int n)
{
    T=NULL;
    itn i =0;
    while(i<n)
    {
        BSTinsert(T,str[i]);
        i++;
    }
}

3.1.5 二叉排序树的删除

删除时不能把这个结点的子树的结点都删除,把删除结点从链表上摘下来,再把断开的二叉链表重新链接起来,同时确保左小右大

删除情况:

(1)若删除结点z是叶结点,直接删除

(2)若z只有一棵左子树或右子树,则让z的子树成为z父结点的子树,代替z的位置

(3)若结点z有左右子树,则z的直接后继或直接前驱代替z,然后从二叉排序树中删去这个直接后继或直接前驱,然后变为1,2中情况

在这里插入图片描述

3.1.6 二叉排序树的查找效率分析

若左右子树高度之差不超过1,ASL=O(log2n)

若只有左/右孩子的单支树,ASL=O(n)

3.2 平衡二叉树

3.2.1 平衡二叉树的定义

为避免树的高度增长过快,减低二叉排序树的性能,规定在插入和删除结点时,保证任意结点的左右子树高度差的绝对值不超过1,这样的二叉树称为平衡二叉树或AVL树

平衡因子:结点左子树与右子树的高度差,则平衡二叉树的平衡因子只可能是-1,0,1

3.2.2 平衡二叉树的插入

保证平衡的基本思想:在插入或删除一个结点时,首先检查插入路径上的结点是否因为此次操作导致不平衡。若不平衡则先找到插入路径上离插入结点最近的平衡因子的绝对值大于1的结点A,再对以A为根的子树,在保持二叉排序树特性的前提下,调整各结点的位置关系,使之重新达到平衡

(1)LL平衡旋转(右单旋转)

在结点A的左孩子的左子树上插入了新结点,需要一次向右的旋转操作,将A的左孩子B向右上旋转代替A成为根结点,将A结点向右下旋转成为B的右子树的根结点,而B的原右子树则成为A结点的左子树

在这里插入图片描述

(2)RR平衡旋转(左单旋转)

在结点A的右孩子的右子树上插入新结点,需要一次向左的旋转操作;将A的右孩子B向左上旋转代替A成为根结点,将A结点向左下旋转成为B的左子树的根结点,而B的原左子树则作为A结点的右子树

在这里插入图片描述

(3)LR平衡旋转(先左后右双旋转)

由于在A的左孩子的右子树插入结点,需要进行两次旋转操作,先左旋转后右旋转。

将A结点的左孩子B的右子树的根结点C向左上旋转提升到B结点的位置,然后把该C结点向右上旋转提升到A结点的位置

在这里插入图片描述

(4)RL平衡旋转(先右后左双旋转)

A的右孩子的左子树上插入新结点,需要进行两次旋转操作,先右旋转后左旋转

将A的右孩子B的左子树的根结点C向右上旋转提升到B结点的位置,然后把C结点向左上旋转提升到A结点的位置

在这里插入图片描述

LR和RL旋转时,新结点究竟是插入C的左子树还是插入C的右子树不影响旋转过程

3.2.3 平衡二叉树的删除

步骤:(1)用二叉排序树的方法对结点w进行删除

(2)若导致不平衡,则从结点w开始向上回溯,找到第一个不平衡的结点z;y为结点z的高度最高的孩子结点;x是结点y的高度最高的孩子结点

(3)然后对以z为根的子树进行平衡调整,其中想x,y和z可能的位置有4种情况:

  • y是z的左孩子,x是y的左孩子(LL)

  • y是z的左孩子,x是y的右孩子(LR)

  • y是z的右孩子,x是y的右孩子(RR)

  • y是z的右孩子,x是y的左孩子(RL)

    插入操作仅需要对以z为根的子树进行平衡调整,删除对以z为根的子树进行平衡调整,如果调整后子树的高度减1,则可能需要对z的祖先结点进行平衡调整,甚至到根结点

在这里插入图片描述

3.2.4 平衡二叉树的查找

与给定值进行比较的关键字个数不超过树的深度,ASL=O(log2n)

3.3 红黑树

3.3.1 红黑树的定义

为了保持AVL树的平衡性,插入和删除后,非常频繁的调整全树整体拓扑结构,代价大,所以在AVL树上引入红黑树

性质:(1)每个结点或是红色,或是黑色

(2)根结点是黑色

(3)NULL结点都是黑色

(4)不存在两个相邻的红结点

(5)对每个结点,从该结点到任意一个叶结点的简单路径长度上,所含黑结点的数量相同

在这里插入图片描述

从某节点出发到达一个叶结点的任意一个简单路径上的黑结点总数称为该结点的黑高(bh),根结点的黑高就是红黑树的黑高

结论1:从根到NULL结点的最长路径不大于最短路径的2倍,最短即全是黑结点

结论2:有n个内部结点的红黑树的高度h$\leqslant$2log2(n+1)

3.3.2 红黑树的插入

插入新结点需要进行着色调整以满足红黑树的性质

结论3:新插入红黑树中的结点初始着为红色

插入过程:

(1)用二叉查找树插入法插入,并将结点z着为红色,若父结点是黑色,无须做任何调整

(2)如果结点z是根结点,将z着为黑色,结束

(3)如果z不是根结点,并且z的父结点p是红色,则分三种情况

情况1:z的叔结点y是黑色的,且z是一个右孩子

LR,先左旋再右旋,z是爷结点的左孩子的右孩子

情况2:z的叔结点y是黑色的,z是一个左孩子

LL,右单旋,z是爷结点的左孩子的左孩子

在这里插入图片描述

若父结点是爷结点的右孩子,则还有两种对称的情况:RL和RR

情况3:如果z的叔结点y是红色

z是左孩子还是右孩子无影响,z的父结点和叔结点都是红色的,因为爷结点是黑色的,把父结点和叔结点着为黑色,爷结点着为红色,然后把爷结点作为新的z来重复循环,指针z在树中上移两层

若父结点是爷结点的右孩子,也还有两种对称情况

只要满足情况3的条件,就会不断循环,每次循环指针z都会上移两层,直到满足第二步,或情况1或情况2的条件

在这里插入图片描述

举例说明:

在这里插入图片描述

以上可总结为如果插入结点违反了不红红,即两个红结点不能相邻,则:

(1)如果是黑叔:旋转+染色

LL:右单旋,父换爷+染色

RR:左单旋,父换爷+染色

LR:左右双旋,儿换爷+染色

RL:右左双旋,儿换爷+染色

(2)如果是红叔:染色+变新

叔父爷染色,爷变为新结点

*3.3.3 红黑树的删除

删除操作容易造成黑高的变化,删除黑结点会导致根结点到叶结点间的黑结点数量减少

删除过程也是先执行二叉查找树的删除方法;若待删结点有两个孩子,不能直接删除,而要找到该结点的中序后继或前驱填补,即右子树中最小的结点,然后转换为删除该后继结点,由于后继结点至多只有一个孩子,这样就转换为待删结点是终端结点或仅有一个孩子的情况。有以下两种情况:

  • 待删结点只有右子树后或左子树
  • 待删结点没有孩子

(1)如果只有右子树或左子树,则有两种情况如图

在这里插入图片描述

子树只有一个结点且必然是红色

(2)如果待删结点没有孩子,若该结点是红色的,直接删除,无须做任何调整

(3)如果待删结点没有孩子,且该结点是黑色;删除该结点会破坏性质5,即黑高应该是一样的,简单的修正方法就是将替换待删结点y的结点x视为还有额外一重黑色,定义为双黑结点,但是这又破坏了性质1,于是删除操作的任务就转化为将双黑结点恢复为普通结点

这又分为四种情况:

情况1:x的兄弟结点w是红色的

w必须有黑色左右孩子和父结点。交换w和父结点x.p的颜色,然后对x.p做一次左旋,现在x的兄弟是旋转之前w的某个孩子结点,其颜色是黑色,情况1就转换成情况2,3,或4处理

在这里插入图片描述

情况2:x的兄弟结点w是黑色的,且w的右孩子是红色的

RR左单旋,即这个红结点是其爷结点的右孩子的右孩子,交换w和父结点x.p的颜色,把w的右孩子着为黑色,并对x的父结点x.p做一次左旋,将x变为单重黑色,此时不成破坏任何性质

在这里插入图片描述

情况3:x的兄弟结点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的

RL先右旋,再左旋,即这个红结点是其爷结点的右孩子的左孩子,交换w和其左孩子的颜色,然后对w做一次右旋,而不破坏红黑树的任何性质,现在x的新兄弟结点w的右孩子是红色,这样就变为情况2

在这里插入图片描述

情况4:x的兄弟结点w是黑色的,w的两个孩子结点都是黑色的

可以从x和w上去掉一重黑色,使得x只有一重黑色而w变为红色,同时把x的父结点x.p额外着一层黑色,以保持局部的黑高不变;通过将x.p作为新结点x来循环,x上升一层。如果通过情况1进入情况4,原来的x.p就是红色,把新结点x变为黑色,终止循环

在这里插入图片描述

若x是父结点x.p的右孩子,则还有四种对称的情况,处理方式类似

总结:情况4中x的兄弟结点w及左右孩子都是黑色,可以从x和w中各提取一重黑色,让x变为普通黑结点,并把调整任务向上推给他们的父结点;

情况1,2,3中兄弟结点w或w的左右孩子有红结点,所以只能在x.p子树内调整和着色,且不能改变x原根结点的颜色;

情况1虽然会转变为情况4,但因为新x的父结点x.p是红色,所以执行一次情况4就会结束;

情况1,2,3在各执行常数次的颜色改变和至多三次旋转后便终止,情况4可能是重复执行的唯一情况,每执行一次指针x上升一层,至多O(log2n)次

举例:

在这里插入图片描述

4 B树和B+树

4.1 B树及其基本操作

m阶B树是所有结点的平衡因子均等于0的m路平衡查找树,可以为空树

性质:

(1)树中么个结点至多有m棵子树,即至多含有m-1个关键字

(2)若根结点不是叶结点,则至少有两棵子树

(3)除根结点外的所有非叶结点至少有 ⌈ \lceil m/2 ⌉ \rceil ,至少含有 ⌈ \lceil m/2 ⌉ \rceil -1个关键字

(4)所有的非叶结点的结构如下:

在这里插入图片描述

(5)所有叶结点出现在同一层,并且不带信息(可以视为外部结点或失败结点,指向的指针为空)

在这里插入图片描述

以上图为例

(1)结点的孩子个数等于该结点中关键字个数加1

(2)根结点没有关键字就没有子树,为空;根结点有关键字,则其子树个数必然大于或等于2,因为子树个数等于关键字个数加1

(3)除根结点外的所有非叶结点至少有 ⌈ \lceil m/2 ⌉ \rceil =5/2=3棵子树,至少含有 ⌈ \lceil m/2 ⌉ \rceil -1=2个关键字,至多5棵子树,至多4个关键字

(4)结点中的关键字从左到由递增有序,关键字两侧均有指向子树的指针,左侧指针所指子树的所有关键字均小于该关键字,右侧指针所指子树的所有关键字均大于该关键字

(5)所有叶结点均在第4层,代表查找失败的位置

4.1.1 B树的高度(磁盘存取次数)

B树操作所需的磁盘存取次数与B树的高度成正比;高度不包含空结点那层

n$\geqslant$1,对任意一棵包含n个关键字,高度为h,阶数为m的B树

(1)关键字个数n ⩽ \leqslant mh-1,因此 h ⩾ \geqslant logm(n+1)

(2)若每个结点的关键字个数最少,则容纳同样多关键字的B树敢赌达到最大

h ⩽ \leqslant logm/2(向上取整)((n+1)/2)+1

4.1.2 B树的查找

与二叉查找树相似,两个基本操作:在B树中找结点;在结点内找关键字

B树一般存储在磁盘上,因此前一个查找操作是在磁盘上进行,后一个在内存中进行,内存中采用顺序查找或折半

4.1.3 B树的插入

(1)定位:利用查找算法找出插入该关键字的最底层中某个非叶结点,会找到查找失败的叶结点,其上一层就是插入位置

(2)插入:每个非失败结点的关键字个数都在区间[ ⌈ \lceil m/2 ⌉ \rceil -1,m-1]内,插入后个数小于m可以直接插入,大于m-1需要进行分裂

分裂:取一个新结点,在插入key后的原结点,从中间位置 ⌈ \lceil m/2 ⌉ \rceil 将其中的关键字分为两部分,左部分包含的关键字放在原结点,右部分包含的关键字放到新结点中,中间位置的结点插入原结点的父结点,若此时父节点的个数也超过上限,继续分裂,直到传到根结点为止,树的高度增1

在这里插入图片描述

4.1.4 B树的删除

要使得删除后的结点的关键字个数 ⩾ \geqslant ⌈ \lceil m/2 ⌉ \rceil -1,设计合并问题

被删关键字不在终端结点,可以用k的前驱或后继来替代,然后删除k

在这里插入图片描述

当被删关键字在终端结点中,有三种情况:

(1)直接删除关键字:个数仍满足条件,可以直接删

(2)兄弟够借:兄弟的结点够多,可调整结点和结点左右兄弟及其双亲结点(父子换位法)达到新的平衡

在这里插入图片描述

(3)兄弟不够借:将关键字删除后与左右兄弟及双亲结点的关键字进行合并

合并过程双亲结点的关键字个数会减1,若又发生了以上情况,重复合并,直到满足B树的性质

4.2 B+树的基本概念

性质:

(1)每个分支结点最多有m棵子树(孩子结点)

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

(3)结点的子树个数与关键字个数相等

(4)所有叶结点包含全部关键字及指向相应记录的指针,叶结点中将关键字按大小顺序排列,并且相邻叶结点按大小顺序相互链接起来

(5)所有分支结点中仅包含它的各个子节点中关键字的最大值及指向其子节点指针

B树和B+树的差异

(1)B+:n个关键字的结点——n棵子树

B:n个关键字的结点——n+1棵子树

(2)B+:结点的关键字个数 ⌈ \lceil m/2 ⌉ \rceil ⩽ \leqslant n ⩽ \leqslant m

B: ⌈ \lceil m/2 ⌉ \rceil -1 ⩽ \leqslant n ⩽ \leqslant m-1

(3)B+:叶结点包含了全部关键字,非叶结点的关键字也会出现在叶结点

B:终端结点和其他结点的关键字不重复

(4)B+:叶结点包含所有信息,所有非叶结点只有索引作用

在这里插入图片描述

5 散列表(哈希表)

5.1 基本概念

之前的查找都是建立在比较的基础上,查找的效率取决于比较的次数。

散列函数:一个把查找表中的关键字映射成该关键字对应的地址的函数,记为Hash(key)=Addr(可以是数组下标,索引或内存地址)

散列函数可能会把两个或两个以上的不同关键字映射到同一地址,称这种情况为冲突,发生碰撞的不同关键字称为同义词

散列表:根据关键字而直接进行访问的数据结构,散列表建立了关键字和存储地址直接的一种直接映射关系

理想情况的时间复杂度:O(1)

5.2 散列函数的构造方法

注意事项:(1)散列函数的定义域必须包含全部需要存储的关键字,而值域的范围则依赖于散列表的大小或地址范围

(2)散列函数计算出来的地址应该能等概率,均匀的分布在整个地址空间中,从而减少冲突的发生

(3)散列函数尽可能简单

常用的几种:

(1)直接定址法

直接取关键字的某个线性函数值为散列地址

H(key)=key或H(key)=a*key+b

计算简单,不会发生冲突,适合关键字分布基本连续的情况,若关键字分布不连续,空位较多,则会造成存储空间的浪费

(2)除留余数法

最简单常用,假设表长为m,取一个不大于m但最接近或等于m的质数p,利用公式把关键字转换成散列地址

H(key)=key%p

(3)数字分析法

假设关键字是r进制数,r个数码在各位上出现的频率不一定相同,可能分布均匀也可能分布不均匀,此时选取数码分布较为均匀的若干位作为散列地址;但是更换了关键字就需要重新构造新的散列函数

(4)平方取中法

关键字的平方值的中间几位作为散列地址,使得散列地址分为较为均匀

5.3 处理冲突的方法

5.3.1 开放定址法

指可存放新表项的空闲地址既向它的同义词表项开放,又向它的非同义词表项开放。数学递推公式为:

Hi=(H(key)+di)%m

i=0,1,2,….k(k ≤ \leq m-1);m表示表长,di为增量序列

取定某一增量序列后,对应的处理方法就是确定的,通常有四种取法:

(1)线性探测法

即di=0,1,2……m-1

冲突发生时,顺序查看表中下一个单元(探测到表尾时,下一个就是表头),直到找出一个空闲单元或查遍全表

造成了大量元素在相邻的散列地址上聚集起来,大大降低了查找效率

(2)平方探测法

di=02,12,-12,22,-22……k2,-k2,k ≤ \leq m/2;m必须是一个可以表示成4k+3的素数,又称二次探测法

可以避免堆积,但是不能探测到散列表的所有单元,至少能探测一半单元

(3)双散列法

当di=Hash2(key)时,称为双散列法

需要使用两个散列函数,当通过第一个散列函数H(key)得到的地址发生冲突时,则利用第二个散列函数Hash2(key)计算该关键字的地址增量

Hi=(H(key)+i*Hash2(key))%m

初始探测位置H0=H(key)%m,i是冲突的次数,初始为0,最多经过m-1次探测就会遍历表中所有位置,回到H0

开放地址不能随便物理删除表中已有元素,若删除会截断其他具有相同散列地址的元素的查找地址,因此要删除时做一个删除标记,进行逻辑删除,但是又会导致散列表表面上看起来很满,实际上有许多位置未利用,因此需要定期维护,把标记的元素物理删除

5.3.2 拉链法(链接法,chaining)

可以把所有的同义词存储在一个线性链表中,这个线性链表由其散列地址唯一标识

假定散列地址为i的同义词链表的头指针存放在散列表的第i个单元中,因而查找,插入和删除操作主要在同义词链中进行

例如关键字{19,14,23,01,68,20,84,27,55,11,10,79},散列函数H(key)=key%13

在这里插入图片描述

5.4 散列查找及性能分析

初始化:Addr=Hash(key)

(1)检测查找表中地址为Addr的位置上是否有记录,若无,返回查找失败;若有记录,比较它与key的值,相等返回成功,否则执行步骤2

(2)用给定的处理冲突办法计算下一个散列地址,并把Addr置为此地址,转入步骤1

举例:

在这里插入图片描述

散列函数:H(key)=key%13

查找84:H(84)=6,但是L[6]$\neq 84 ,则找第一次冲突处理的地址 H   1   = ( 6 + 1 ) 84,则找第一次冲突处理的地址H~1~=(6+1)%16=7,L[7] 84,则找第一次冲突处理的地址H 1 =(6+1)\neq$84,则找第二次冲突处理的地址H2=(6+2)%16=8,L[8]=84,成功并返回8

同一组关键字,设定相同的散列函数,不同的处理方法得到的散列表不同,ASL也不同

(1)冲突的产生导致散列表的查找过程仍然是一个给定值和关键字进行比较的过程,就需要以ASL来衡量散列表的查找效率

(2)查找效率取决于:散列函数,处理冲突的方法和装填因子

装填因子 α \alpha α= 表中记录数 n 散列表长度 m \dfrac{表中记录数n}{散列表长度m} 散列表长度m表中记录数n,定义为一个表的装满程度,ASL就依赖于 α \alpha α,而不直接依赖n或m

α \alpha α越大装填的记录越满,冲突的可能性越大

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/667821.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#提升(一、泛型)

一、什么是泛型 泛型&#xff0c;即“参数化类型” 我们来看以下代码&#xff0c;目的很明确&#xff0c;就是显示参数类型&#xff0c;这种类似的代码或者说只有参数类型不同&#xff0c;但是功能相同时&#xff0c;我们如何让代码写的更优雅&#xff1f; 在泛型没有出现的…

LaTeX花式引用章节、图片、公式【有图有代码】

LaTeX花式引用章节、图片、公式【有图有代码】 1 使用~\cite, ~\cref, ~\autoref~\cref~\autoref~\ref 1 使用~\cite, ~\cref, ~\autoref 为什么要使用~ 因为 ~ 符号起到限制换行的作用&#xff0c;通常情况下&#xff0c;LaTeX会根据需要自动确定在引用标签和编号之间的换行点…

Flink 学习二 Flink 编程基础API

Flink 学习二 Flink 编程基础API 1. 基础依赖引入 <dependency><groupId>org.apache.flink</groupId><artifactId>flink-java</artifactId><version>1.14.4</version></dependency><dependency><groupId>org.apa…

MacOS安装与卸载Zookeeper

文章目录 安装1.下载2.移动至/usr/local 目录下3.进入 ZooKeeper 目录4. 拷贝出一份新的配置文件5.启动 ZooKeeper 服务器6.验证 ZooKeeper 是否成功启动 关闭卸载参考 安装 1.下载 https://downloads.apache.org/zookeeper/zookeeper-3.7.1/ 2.移动至/usr/local 目录下 将…

驻波比理解

VSWR(Voltage Standing Wave Ratio)代表电压驻波比。要完全理解这个术语&#xff0c;需要知道什么是“驻波”。 假设两个波长相同的波以相反的方向传播&#xff0c;如下所示。一个波表示为蓝线&#xff0c;它朝着正确的方向旋转。另一个波用绿线表示&#xff0c;它在左方向旋转…

Android大图加载优化方案

我们在编写Android程序的时候经常要用到许多图片&#xff0c;不同图片总是会有不同的形状、不同的大小&#xff0c;但在大多数情况下&#xff0c;这些图片都会大于我们程序所需要的大小。比如微博长图&#xff0c;海报等等。所以我们就要对图片进行局部显示。 大图加载基本需求…

【QQ界面展示-监听键盘事件 Objective-C语言】

一、关于这个通知,我们就说到这里, 1.接下来,就看一下, 我们说了这么一堆,目的是为了什么, 目的是为了监听我们那个键盘的点击事件吧, 我们说了一堆,目的是为了监听我们这个键盘的弹出事件、不是点击事件, 当键盘弹出以后,我们是不是要做一件事儿, 那么,我们知道…

虚拟机网卡/网络配置,静态IP配置

文章目录 1. Vmvare设置 “编辑->虚拟机网络编辑”2. 新建一个虚拟机并给它设置网卡3. 配置eth0网卡为静态IP vim /etc/sysconfig/network-scripts/ifcfg-eth04、测试 1. Vmvare设置 “编辑->虚拟机网络编辑” 这里设置了3个虚拟网络(两个主机模式&#xff0c;这两个网络…

2023.6.20 GPIO子系统编写LED驱动

作业&#xff1a;通过GPIO子系统编写LED驱动&#xff0c;应用程序控制LED灯亮灭 &#xff08;1&#xff09;led.h #ifndef __LED_H__ #define __LED_H__ // typedef struct{ // unsigned int MODER; // unsigned int OTYPER; // unsigned int OSPEEDR; // un…

FreeRTOS实时操作系统(五)临界区及任务调度器

系列文章目录 文章目录 系列文章目录临界区代码保护任务调度器的挂起与保护 临界区代码保护 临界区&#xff1a;是指那些必须要完整运行的&#xff0c;不能被打断的代码 适用于&#xff1a; 1.外设初始化 2.操作系统的代码有很多不能被打断 3.用户自己的需求 一般在中断、任…

014 - STM32学习笔记 - I2C访问存储器(一)

014 - STM32学习笔记 - I2C访问存储器 1、存储器分类 存储器主要分为两类&#xff1a;易失性存储器和非易失性存储器&#xff0c;从字面上理解&#xff0c;判断易失/非易失主要取决于设备掉电后&#xff0c;存储的数据是否会丢失。常规的来说&#xff0c;易失性存储器存取速度…

Django基础入门⑥:Django过滤器和标签讲解

Django基础入门⑥&#xff1a;Django过滤器和标签讲解 Django过滤器过滤器语法过滤器应用获取变量的长度截取指定个数的词返回指定键的排序列表add给变量值加“n” Django url标签url标签动态url Django自定义标签如何自定义标签定义之前的准备工作模块变量register自定义标签赋…

Java 对接google WIFI定位API

1.创建Http请求工具类 1.1.引入httpclient <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.10</version></dependency> 1.2.封装Http工具类 /*** Http请求* a…

MySQL 高级语句 一

目录 一、MySQL高级&#xff08;进阶&#xff09;SQL语句1. select2. distinct3. where4. and or5. in6. between7. 通配符8. order by9. 函数9.1 数学函数9.2 聚合函数9.3 字符串函数 二、高级查询语句2.1 group by &#xff08;用于分组和汇总&#xff09;2.2 having2.3 别名…

如何在 XMind 中绘制流程图

XMind 是专业强大的思维导图软件,由于其结构没有任何限制,很多朋友特别喜欢用它来绘制流程图。禁不住大家的多次询问,今天 XMind 酱就将这简单的流程图绘图方法分享给大家。 在 XMind 中,绘制流程图的主角是「自由主题」和「联系」。它们可以打破思维导图的限制,让你自由…

《异常检测——从经典算法到深度学习》21 Anomaly Transformer:具有关联差异的时间序列异常检测

We # 《异常检测——从经典算法到深度学习》 0 概论1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法3 基于One-Class SVM的异常检测算法4 基于高斯概率密度异常检测算法5 Opprentice——异常检测经典算法最终篇6 基于重构概率的 VAE 异常检测7 基于条件VAE异常检测8 Do…

[NX亲测有效]Ubuntu,Jetson nano,NX板开机设置开机自起,Jetson nano,NX设置x11vnc开机自起

&#xff01;&#xff01;Ubuntu,Jetson nano,NX板开机设置开机自起&#xff0c;nano NX设置x11vnc开机自起&#xff01;&#xff01; 1.创建一个rc-local自启服务 2.创建运行脚本 3.启动服务 4.NX&#xff0c;nano设置x11vnc并设置开机自启 大功告成&#xff01;编写不易…

【中级软考】软件设计-考试介绍

一、软考好处 通过软考认证可以抵扣当年的 3600 元的个税,并且有些城市可以积分落户,同时获得证书可以获得同等级别的职称。计算机方向的职称是以考代评,所以获得中级软考证书就相当于获得同等的中级计算机工程师职称,获得高级软考证书就相当于获得同等的高级计算机工程师…

如何使用@umijs/plugin-qiankun搭建微前端项目

umijs/plugin-qiankun是一个基于UmiJS框架的插件&#xff0c;用于实现乾坤微前端架构。乾坤微前端是一种前端架构模式&#xff0c;可以将一个大型的前端应用拆分成多个小型的子应用&#xff0c;每个子应用可以独立开发、独立部署、独立运行&#xff0c;同时可以通过乾坤框架进行…

ArduPilot开源代码之AP_InertialSensor

ArduPilot开源代码之AP_InertialSensor 1. 源由2. AP_InertialSensor类2.1 init2.2 periodic2.3 update 3. 重要应用方法3.1 BatchSampler::push_data_to_log3.2 wait_for_sample3.2 calibrate_gyros 4. 总结5. 参考资料 1. 源由 前面研读了IMU如何通过front-end/back-end获取…