当前位置: 首页 > news >正文

查找及其算法

基本概念

查找:

  • 在数据结构中寻找某个满足要求的数据的过程;

查找表:

  • 用于查找的数据集合(数据结构)

关键字:

  • 数据元素中唯一标识该元素的某个数据项的值;

查找长度:

  • 在查找过程中,需要对比关键字的次数;

平均查找长度:

  • 所有查找过程中进行关键字比较次数的平均值
  • 通过这个指标可以很好的反映出二叉排序树(BST)相比于其他树的优越性;

顺序查找

从头到尾或者反过来遍历并查找,适用于线性表;

优化

如果将线性表中的元素顺序存储,就可以在某些案例中缩短查找长度;


例如:线性表中按升序存有12,24,35,46,67,88,96,105,111,当我们需要查找数字55的时候,遍历到67的时候就可以退出遍历循环;


优化后顺序查找的平均查找长度为n/2 + n/(n+1),这个优化思路优化了查找失败时的平均查找长度。


另外一种优化思路则是将被查找的概率大的数据放在前面,这样可以优化查找成功的平均查找长度

二分查找

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

//查找表的数据结构(升序排列)
struct STable
{Elemtype* base;int len;
}//二分查找
int binary_search(STable L, ELemtype target)
{int low = 0;int heigh = L.len -  1;int mid;whlie(low <= high){mid = (low + high) / 2;if(*(base + mid) == target)){return mid;}if(*(base + mid) > target){high = mid - 1;}else{low = mid + 1;}}return -1;
}

查找效率分析

在这里插入图片描述

如图所示为二分查找的一个判定树,在这个例子中成功的ASL为(1*1 + 2*2 + 3*4 + 4*4)/11 = 3,查找失败的ASL为(3*4 + 4*8)/12 = 11/3

判定树的性质:

通过计算可以发现,若low与high之间有奇数个元素,则mid分割之后两边的元素数量相等,反之如果有偶数个则mid分割之后右边的元素比左边的元素多一个;

  • 即它是一棵平衡二叉树

所以我们可以得出结论:二分查找的判定树的任意一个结点的右子树的节点数量总是比左子树的多一个或者相等;

  • 这个结论适用于除法向下取整,如果向上取整则结论刚好相反;

判定树的树高(不包含失败结点)为log2(n+1);
所以查找的时间复杂度为O(log2n);

分块查找

在这里插入图片描述

如图,将查找表中无序的元素分成若干块,分别对应区间[0,10],(10,20],(20,30],(30,40],(40,50],再结合索引表来查询。


可以看出,索引表的特点是块内无序块间有序,在块内我们使用顺序查找,查询块间索引时除了顺序查找还可以使用二分查找;


分块查找的索引结构如下:

//索引表
struct index
{elemtype max;int low;int high;
};//查找表
elemtype list[value];

二分查找查找索引表

  • 如果需要查找的元素就是索引表中元素则可以通过二分查找直接找到
  • 如果不是索引表中的元素,最后会因为low > high而退出,这时候我们顺序查找low所指向的分块即可;
  • 越界问题:如果因为要查找的元素小于索引表中最小的元素,则最终还会应为low <= high的判断而退出,不影响程序执行。如果要查找元素大于索引表的最大元素则可以直接判断该元素不存在;
int binary_search_index(index idx[], elemtype list[], int index_num, elemtype target)
{int min = 0;int top = index_num - 1;if(idx[top].max < target)return -1;while(min <= top){mid = (min + top) / 2;if(idx[mid].max > target){top = mid - 1;}else if(idx[mid].max < target){min = mid + 1;}else{for(int i = idx[mid].low; i <= idx[mid].high; i++){if(list[i] == target)return i;}}}for(int i = idx[min].low; i <= idx[min].high; i++){if(list[i] == target)return i;}return -1;
}

查找效率分析

  • 对于查找成功的ASL只需要逐个计算即可;
  • 查找失败的ASL只在以下特殊情况中方便计算出

在这里插入图片描述

将查找表中的n个元素平均分成b块,每块中有s个元素;

  • 索引查找和块内查找的平均查找长度分别为L1,Ls;则ASL = L1 + Ls;

顺序查找:

  • L1 = (1 + 2 + ... + b) / b = (b + 1) / 2, Ls = (1 + 2 + ... + s) / s = (s + 1) / 2;
  • ASL = L1 + Ls = (s^2 + 2s + n) / 2s; s = n的平方根时ASL最小;

存储结构的优化

在这里插入图片描述

如图,通过链式存储索引表可以让添加或者删除数据的操作变得更加方便;

二叉查找树BST

  • 左子树上的所有结点都小于根节点;
  • 右子树上的所有节点都大于根节点;
  • 左右子树又同样是一颗BST;

中序遍历BST就可以得到一个有序序列

//二叉树的节点结构
struct Node
{int data;Node* right_child;Node* left_child;Node(int n){data = n;right_child = NULL:left_child = NULL;}
}

插入新节点

bool insert_BST(Node* &T, int n)
{Node* i = new Node(n);if(T = NULL){T = i;return true;}if(T->data == n){return false;}else if(T->data > n){insert_BST(T->left_child, int n);}else{insert_BST(T->right_child, int n);}delete i;return true;
}

构建二叉查找树

Node* build_BST(int list[], int len, Node* &T)
{for(int i = 0; i < len; i++){insert_BST(T, list[i]);}
}

删除二叉查找树的某个节点

  • 如果删除的节点是叶子节点则直接删除;
  • 如果删除的结点只有一个左子树或者右子树则直接删除并将子树连接给双亲结点;
  • 如果删除的结点既有左子树也有右子树那么可以用左子树中最小的结点或者右子树中最大的结点代替要删除的结点;
//查找某个结点
Node* &search(Node* T, int n)
{search(T->left_child, n);if(T->data == n || T = NULL){return T;}search(T->right_child, n);
}
//查找双亲结点
Node* &search(Node* T, Node* son)
{if(T == son)	//根节点就是目标结点的情况return T;search(T->left_chlid, son);if(T->right_child == son || T->left_child == son || T == NULL){return T;}search(T->right_child, son);
}bool delete_node(Node* &T, int n)
{Node* target = search(T, n);Node* parent = search(T, target);if(target == NULL || parent == NULL){return false;}if(target->right_child == NULL && target->left_child == NULL){if(target == parent){T = NULL;return true;}if(parent->right_child == target){parent->right_child = NULL;return true;}else{parent->left_child = NULL;return true;}}if(target->right_child != NULL && target->left_child == NULL){if(target == parent){T = target->right_child;return true;}if(parent->right_child == target){parent->right_child = target->right_child;return true;}else{parent->left_child = target->right_child;return true;}}if(target->right_child == NULL && target->left_child != NULL){if(target == parent){T = target->left_child;return true;}if(parent->right_child == target){parent->right_child = target->left_child;return true;}else{parent->left_child = target->left_child;return true;}}Node* new_node = target->right_child;while(new_node->left != NULL){new_node = new_node->left_child;}Node* temp = search(T, new_node);temp->left_child = NULL;new_node->left_child = target->left_child;new_node->right_child = target->right_child;if(parent->left_child == target){parent->left_child = new_node;}else{parent->right_child = new_node;}

平衡二叉树AVL

根据并查集的性质可知,树的高度越低我们查找的效率也就越高,我们把二叉查找树的每个结点的左右子树的高度差都控制在一个以内就能保证树的高度最低;

  • 平衡因子:左子树的高度减去右子树的高度;

中序遍历AVL可以得到一个升序的序列

平衡二叉树的插入

在AVL中插入元素之后可能会导致树的结构发生变化,平衡性被破坏。平衡性被破坏的情况可以分为以下四种:
在这里插入图片描述
调整不平衡的AVL我们只需要调整最小不平衡子树即可;
在这里插入图片描述

平衡二叉树的删除

对AVL的删除操作同样可能导致平衡性被破坏,需要以下步骤恢复平衡性:

  • 删除节点(操作和删除二叉查找树的操作相同);
  • 从删除的结点开始向上寻找最小的不平衡子树(如果存在);
  • 找到不平衡子树高度最高的孩子节点,孙子节点;
  • 根据孙子结点的位置以及四种不平衡类型调整子树;
  • 如果调整之后仍不平衡则重复上述步骤接着调整;

找到最小不平衡子树之后调整的方法实际上除了可能需要重复处理之外与插入之后调整的步骤完全一致,操作方法可以类比插入之后调整子树的方法;

查找效率分析

若用nh表示深度为h的平衡树中最少的结点树则有:nh = n(h-1) + n(h - 2) + 1; 结点最少的高度为h的平衡树可以看作是一个结点数最少的高度为h - 1的平衡树加上结点数最少的高度为h - 2的平衡树加一个根节点组成的;

  • 含有n个结点的平衡树的最大深度为logn,平均查找长度为logn;

红黑树RBT

AVL虽然查找或者删除插入的时间效率比较高,但是需要频繁地调整树的形态时间开销较大;RBT的性能和AVL一样,但是插入删除操作一般不会破坏红黑特性,即使被破坏需要调整也可以在常数级的时间复杂度内完成;


RBT也是一种二叉排序树,但相对于普通的二叉排序树红黑树有以下特点:

  • 每个结点是红色或黑色;
  • 根节点必须是黑色;
  • 红黑树的叶节点是外部结点NULL(即失败结点),并且所有叶节点都是黑色的;
  • 不存在两个相邻的红结点(有边直接连接才算相邻);
  • 对于每个节点,从该结点到任意叶节点的简单路径上所含黑色结点的数量都相同

红黑树结点的结构:

struct RBT_Node
{int key;RBT_Node* parent;RBT_Node* left_child;RBT_Node* right_child;int color;	//结点颜色,0代表黑色,1代表红色
};

在这里插入图片描述

黑高:从结点出发(不含该结点)到达任意一个叶结点的路径上的黑结点总数

  • 如果根节点的黑高为h,内部结点最少为2^h - 1个结点(当该红黑树为一个全部是黑色结点的满树时)

性质

  • 从根节点到叶结点的最长路径不大于最短路径的两倍;
  • 有n个内部结点的红黑树高度 h <= 2log2(n + 1);

性质一是因为每个节点到叶节点路径上的黑结点数是一样的并且红色结点不相邻,所以红色的节点只能穿插在黑色节点之间;


由性质二可以知道红黑树的查找的时间复杂度为O(log2n);

红黑树染色的意义:

从红黑树的特点以及性质来看,我们不难发现RBT为每个节点染色实际上是为了维持每条路径的长度

  • 首先黑路同的特点就保证了每条路径上的黑色节点数量一定是一样的;
  • 其次不红红的特点则保证最长的路径一定不会大于最短的路径的两倍:
    – 因为红色结点只能穿插在黑色结点之间,最短的路径是只有黑色结点的路径;
  • 也正是对路径长度的控制保证了红黑树不会退化成链:
    – 因为不会出现一条路径的长度远远大于另一条路径的情况;

红黑树的插入

步骤:

  • 先查找,确定插入位置,插入新节点。若新节点为根则染为黑色,如果是非根则染为红色;

插入后若不满足红黑树定义则做以下调整:

  • 如果父结点的兄弟结点为黑色,则染色加旋转:
    – LL:右单旋,父亲结点和爷爷结点交换位置并且染色;
    – RR:左单旋,父亲结点和爷爷结点交换位置并且染色;
    – LR:左,右双旋,孩子结点和爷爷结点交换位置并染色;
    – RL:右,左双旋,孩子节点和爷爷结点交换位置并且染色;
  • (LL是指新插入的结点是父节点的左孩子,父节点是爷爷结点的左孩子,染色是指把最终交换的两个结点染成另一种颜色);
  • 若父结点的兄弟结点为红色,则:
    – 叔叔结点,父亲结点,爷爷结点都染色,爷爷结点变成新节点,用处理新结点的方式处理爷爷结点(即重复上述步骤,如果是根节点则直接染黑)

总结

为什么要让新插入的结点都为红色:

  • 为了保证插入之后,一个结点到任意一个叶节点的路径上的黑色结点的数量不变;

红黑树的删除

  • 红黑树删除操作的时间复杂度为:O(log2n);
  • 红黑树中删除结点的处理方式和二叉排序树的删除方法相同;
  • 如果删除之后破坏了红黑树的特性则需要再次调整;

在学习红黑树的删除操作之前,我们首先学要明确一下红黑树的删除是如何进行的:

  • 以下图为例子,我们要删除结点18的步骤为:用23代替18,不改变之前18所在结点的颜色,然后删除结点23;
  • 或者用17代替18,18原来所在的结点的颜色不变,删除结点17;

不难发现,我们实际上要删除的是用来替代目标结点的结点,并且这些结点的子树至多含有一个结点(不包括NULL代表的叶节点)图中为了更加简洁没有画出叶节点;
在这里插入图片描述
并且我们新加一个定义:双黑结点

  • 什么意思呢:就是说所有经过双黑结点的路径的黑色节点数量都比正常的路径少一个,双黑结点的意思呢就是代表这个地方应该有两个黑色节点的但是实际上只有一个;

只有左孩子或者有孩子

这种类型之可能出现两种情况:
在这里插入图片描述
如果父节点是红色,那么他只有一个黑色节点就天然违反了黑路同的性质,所以是不可能出现的;


这种情况的处理也很简单,只需要删除黑色结点,让红色节点去替代黑色结点并且将红色结点染成黑色节点;

没有孩子(不包括叶子节点)

要删除的是红色节点

因为红色节点是否存在都不会影响黑路同的性质,所以直接删除红色结点即可;

要删除的是黑色节点

这种情况最为复杂,还需要双黑结点的兄弟结点来判断;

兄弟是黑色,并且至少有一个红色节点

如下图,我们要删除结点12
在这里插入图片描述
删除后树的情况:
在这里插入图片描述
删除之后p的右子树只剩下一个叶节点,所有经过这个叶结点的路径黑节点数都会少一,我们将这个节点标记为双黑节点,他的兄弟有一个红色的左孩子,这种情况我们归为LL型,它的调整方式为:将r的颜色染为s的颜色,s的颜色染为p的颜色,p节点变为黑色将s节点右旋代替p。调整完成RBT即恢复正常,双黑节点变为正常的节点;
调整之后的树:
在这里插入图片描述


调整逻辑:

  • 首先说为什么旋转:旋转实际上是将双黑结点的父节点降级,这样实际上是延长了双黑节点所在的路径的长度(通过增加黑色节点)
  • 染色的原理:这一点则与红黑树染色的意义有关,上述方法中的染色方式实际上是为了旋转之后通过颜色维持的层级关系不变,正如图二所示旋转实际上是蓝色部分替代了之前的绿色部分,父节点则降级

知道了LL型的调整逻辑,那么RR型也是同理,将r染色为s的颜色,再将s染色为p的颜色,p变成黑色s左旋替代父节点即可。下图是我们需要调整的RR型一个例子,删除节点2之后叶节点变成双黑节点;在这里插入图片描述
调整之后:
在这里插入图片描述


除了RR型,LL型,还有RL型以及LR型,它们又是另外一套调整逻辑;
如下图是一个LR型的删除案例,我们需要删除节点12
在这里插入图片描述
删除之后叶节点变成双黑节点
在这里插入图片描述
这里我们的处理方式是:将r节点的颜色染为p结点的颜色,p节点染为黑色,将r节点左旋之后再次右旋。完成时候RBT即恢复正常,双黑节点恢复为正常节点;
在这里插入图片描述

处理逻辑:

  • 和RR型,LL型相同,进行的两次旋转也是为了让蓝色部分替代绿色部分,进而让p节点降级,恢复黑路同的性质,只是因为节点位置的不同让旋转的方式不同;
  • 染色逻辑:通过观察不难发现两次旋转并没有改变s的层级但是让r的层级变成了p的层级,所以r需要被染成p的颜色,p必须要染黑则是为了让它能通过降级消除双黑节点;

RL型也是相同的处理逻辑,这里不做赘述;

兄弟是黑色,并且两个孩子都是黑色

如下图,我们需要删除节点9,他的兄弟节点为两个黑色的叶节点:
在这里插入图片描述
删除之后,叶节点变为双黑节点:
在这里插入图片描述
这里我们首先将节点6染为红色节点
在这里插入图片描述
此时可以发现,节点8左右两边的路径黑色节点都少一个,所以节点8变成了新的双黑节点:
在这里插入图片描述
此时我们发现双黑节点的兄弟节点18满足RR型,我们再次按照RR型的处理方式处理一次即可
下面这种情况则需另外一种处理方式,在下图中我们需要删除节点28:
在这里插入图片描述
经过我们染色并转移双黑节点之后树变为:
在这里插入图片描述
可以看到,红色的节点变为了双黑节点,而双黑结点的含义又是经过这条路的所有路径黑高都少一,所以我们只需要将该红色节点染为黑色即可;


另外还有一种特殊情况:双黑节点移动到了根节点

  • 那么所有经过根节点的路径的黑高都会比之前少一,但实际上已经满足了RBT的要求,两边都少一但黑高仍然是相同的,所以之后就不用再做处理;
兄弟是红色节点

如果兄弟是红色节点那么这个红色节点一定分别有一个黑色节点作为左右孩子,否则将会天然不满足黑路同的原则;
如下图,我们需要删除节点21在这里插入图片描述
删除之后叶节点变成双黑节点:
在这里插入图片描述
此时我们处理的方式为:==交换s和p的颜色,然后将s向双黑节点所在的位置旋转,图中为向右旋转。==此时p节点已经降级,但因为p没有强制要求变成黑色,所以黑色节点并没有增加,我们还需要继续处理
在这里插入图片描述
此时,双黑结点的兄弟节点为有两个黑色孩子的黑色节点,这是我们之前讨论过的情况只需要按照之前的处理方式再次处理一次即可;

B树

在这里插入图片描述

B树是多路平衡查找树,B树可以是空树,B树种孩子数最多的节点的孩子数量为B树的阶,m阶B树应该满足以下条件应该满足以下条件:

  • 树中的每个节点至多有m颗子树,至多含有m - 1个关键字;
  • 若根节点不是终端节点则至少有两个子树;
  • 除根节点之外的所有非叶节点至少有m/2(向上取整)颗子树;
  • 所有的叶节点都在同一层,并且叶节点不含任何信息(因为B树的叶节点是失败节点,指向这些失败节点的指针为空)

总结上述内容可以得到B树的核心特点:

  • B树的每个节点的左右子树的高度一定是一样的,这样一方面可以压缩树的高度,另一方面还可以防止树退化成链;
  • 对除了根节点之外的非叶节点的关键字做出的数量限制是为了保证树的高度不会太高,从而优化了查找效率;

含有n个关键字的m阶B树的最小高度(不含叶子节点),最大高度

要计算最小高度只需要让每个节点包含尽可能多的关键字即可

  • 根节点有m-1个关键字,则下一层有m个节点,每个节点有m-1个关键字所以:m - 1 + m * (m - 1) + m^2 * (m - 1) + ... + m^h * (m - 1) >= n
  • 得到:在这里插入图片描述

要使高度最大同样的只需要让每个节点所含的关键字最少,除了根节点含有两个关键字每个节点都含有m/2 个关键字

  • 第一层有一个节点,第二层有2个节点,第三层有2*(m/2 + 1)个节点,第h层有2 *(m/2 + 1)^(h - 2)个节点,同时因为有n个关键字,则叶节点有n + 1个;
  • 综上有关系:在这里插入图片描述
  • 得到最高的高度:
    在这里插入图片描述

B树的插入

在B树中插入key之后,如果导致原节点的关键字数量超过了上限则需要通过分裂完成平衡,具体操作为:

  • 从m/2+1位置的关键字将关键字分为两部分,左边的部分放在原节点中,右边的部分创建为新节点中间位置的节点插入到原结点的父节点。
  • 如果上述操作导致父节点的关键字也超出限制则对父节点也进行相同的分裂操作。
  • 如果因为上述操作根节点的关键字超出限制则将根节点也进行分裂操作,不同的是根节点m/2+1位置的关键字向上直接变成新的根节点;

可以发现,每次通过“分裂”的操纵调整树
可以让新增加的节点出现在原来结点的同一层,这样保证了所有节点的子树的高度是一样的;


并且要注意的是每一次插入一定是在终端节点进行的,否则可能导致叶节点不在同一层从而违背B树的特性;

B树的删除

若删除的关键字不在非终端节点中,则用直接前驱或者直接后继替代即可,操作与BST等相同;

  • 不难发现,B树的删除其实最终也转化成了对终端节点中关键字的删除,所以我们重点讨论对终端节点中关键字的删除;

如果删除终端节点中的一个关键字之后,关键字树任然满足要求则删除完成;


如果删除之后节点中的关键字数不满足要求我们则需要分为多种情况调整;

1.兄弟节点的关键字字数足够借用

这种情况我们借用兄弟结点的一个关键字去顶替父节点中的一个关键字,再从父结点中将被顶替的关键字放入那个关键字字数不足的孩子节点即可,这些操作选用哪一个关键字还需要视具体大小关系而定;

2.兄弟节点的关键字字数不够借用

  • 这种情况我们需要将关键字不足的节点和他的兄弟节点合并,并且合并还需要将它们的父结点中左右指针分别指向它们的关键字一起合并到一个节点中
  • 这种操作会让父结点中的关键字数减一从而导致关键字可能不符合要求,这时我们根据父节点的兄弟结点的关键字情况再做调整即可;

B+树

在这里插入图片描述

如图为一颗B+树,一颗m阶B+树有以下特点:

  • 结点数与关键字数相同(与B树最大的区别)
  • 每个分支节点最多有m颗子树;
  • 除了非叶根节点至少有两颗子树,其他节点(除了叶子节点)至少有m/2(向上取整)颗子树(什么是非叶根节点:当一颗B+树只有一个结点的时候这个结点既是根节点又是叶子节点,允许只有一个关键字);
  • 所有的叶节点包含全部的关键字及指向相应记录的指针,叶节点中将关键字按大小顺序排序,并且相邻叶节点按大小顺序相互连接起来;

不难发现,B+树具有和分块查找相似的结构特点,但是有和B树一样要确保所有结点的左右子树在高度一致并且叶子节点全部都在一层;
并且B+树的插入删除操作和B树相同;


B+树有两种查找方式,一种是从树的根节点开始查找,另一种则是从指向p的结点开始顺序查找;

总结

在B+树中非叶结点仅起索引作用,不含有该关键字指向的记录的关键字;而B树的结点都包含了关键字对应记录的存储位置;
B+树结点的分指数与关键字树相同,B树结点的关键字树比分支数少一;

哈希表

哈希表是一种新的数据结构,特点是可以根据关键字计算出它在散列表中的存储地址;

  • 哈希表通过哈希函数计算出每个关键字的储存地址,建立了从关键字到存储地址的映射关系;

冲突

指在散列表中插入一个数据元素时通过关键字计算出的储存地址已经储存了其他元素;

  • 冲突发生的越少,哈希表的性能越高;

通过构造更好的哈希函数可以尖山冲突

哈希函数的构造

常见的哈希函数构造方法有:

  • 除留取余法,直接定址法,数字分析法,平方取中法;
    哈希函数的要求:
  • 定义域必须涵盖所有关键字;
  • 值域不能超过哈希表的地址范围;
  • 尽可能减少冲突,哈希函数计算出来的地址应该尽可能均匀的分布在整个地址空间;
  • 哈希函数应该尽可能简单,能够快速计算出关键字对应的地址;

除留取余法

H(key) = key % p;

  • 设散列表长度为m,取一个不大于但最接近或等于m的质数p;

直接定址法

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

  • 适用于关键字比较连续的情况,如果关键字不连续则会造成空间浪费;

数字分析法

选取关键字数码分布较为均匀的若干位作为哈希地址;

  • 设关键字是r进制数字,而r个数码在各个位置上出现的频率不一定相同,可能在某几位上分布均匀一些,即每种数码出现的概率均等;就可以选取这几位作为哈希地址;

– 例如手机号前三位比较固定,每个数字出现的机会不均等,而后四位则比较随机每个数字出现的概率均等,可以选取后四位当作哈希地址

平方取中法

取关键字的平方值的中间几位作为哈希地址;

  • 适用于关键字的的每位取值都不均匀的情况;

处理冲突的方法

拉链法

在这里插入图片描述

如图,该方法就是将同义词用链表的方式存储起来

开放定址法

原理:

  • 如果发生冲突,就给新元素找另一个空闲位置,所以在这种逻辑下一个散列地址既对同义词开放,也对非同义词开放
  • 第i次发生位置冲突时的哈希地址:Hi = (H(key) + di) % m;m为哈希表的长度di是指第i次发生冲突时的偏移量
    方法:
  • 线性探测法,平方探测法,双散列法,伪随机序列法

在这里插入图片描述

开放定址法的查找也是相同的方法,如果查找到的地址为空则查找失败;


需要注意的是删除操作:删除操作不能直接将哈希表中对应位置的元素直接删除,否则可能导致在开放定址法的方法下查找同义词失败(因为前面提到查找到空说明查找失败),我们可以定义一个元素来标记哈希表的某个位置的元素是否被删除


对于开放定址法还有一个结论:

  • 如果哈希表长度m可以表示成4j+3的素数,平方探测法就能探测到所有位置;
  • 如果hash2(key)计算得到的值与表长m互质,就能保证双散列法可以探测到所有单元;
    – 若表长m本身为质数,hash2 = m - (key%m);无论key是多少计算出的值一定是互质的,因为质数与所有比自己小的数互质;

哈希表的性能分析

装填因子:
在这里插入图片描述

  • 装填因子越大,越容易发生冲突。从而导致插入,查找操作效率降低,ASL增大;

堆积现象:
在处理冲突过程中,几个初始散列地址不同的元素争夺同一个后继散列地址的现象;
在这里插入图片描述

http://www.dtcms.com/a/560933.html

相关文章:

  • Java 高级特性:泛型与包装类深度解析
  • GD32F407VE天空星开发板的旋转编码器EC12的实现
  • 从零开始学习Redis(五):多级缓存
  • 解码LVGL样式
  • 山西响应式网站建设价位企业培训计划
  • 深入浅出 C++ 多态:从概念到原理
  • 多实现类(如IService有ServiceA/ServiceB)的注入配置与获取
  • web自动化测试-Selenium04_iframe切换、窗口切换
  • 分类与回归算法(一)- 模型评价指标
  • 浙江十大建筑公司排名用v9做网站优化
  • 江门网站建设自助建站站内seo和站外seo区别
  • 嵌入式Linux:线程同步(自旋锁)
  • RHCE复习第一次作业
  • 2025年山西省职业院校技能大赛应用软件系统开发赛项竞赛样题
  • 铁路机车乘务员心理健康状况的研究进展
  • 人才市场官方网站装修公司网站平台
  • Flink 2.1 SQL:解锁实时数据与AI集成,实现可扩展流处理
  • 【软件安全】什么是AFL(American Fuzzy Lop)基于覆盖率引导的模糊测试工具?
  • 山西省最新干部调整佛山网站建设优化
  • 背包DP合集
  • Docker 拉取镜像:SSL 拦截与国内镜像源失效问题解决
  • full join优化改写经验
  • 软件测试:黑盒测试用例篇
  • 【Linux】Linux第一个小程序 - 进度条
  • ubuntu新增用户
  • 青州市网站建设长沙招聘网58同城招聘发布
  • 江苏中南建设集团网站是多少长沙互联网网站建设
  • 从零开始的云原生之旅(十一):压测实战:验证弹性伸缩效果
  • 民宿网站的建设wordpress gallery
  • 【开题答辩全过程】以 广州网红点打卡介绍网站为例,包含答辩的问题和答案