数据结构十:哈希表

        本次将从概念上理解什么是哈希表,理论知识较多,满满干货,这也是面试笔试的一个重点区域。

目录

一、什么是哈希表

1.0 为什么会有哈希表?

1.1 哈希表的基本概念

1.2 基本思想

1.3 举例理解

1.4 存在的问题

1.5 总结

二、哈希函数的构造方法

2.1 哈希函数的理解

2.2构造方法

2.2.1 直接定址法

2.2.2 数字分析法

2.2.3 平方取中法

2.2.4 折叠法

2.2.5 除留余数法(最常用)

2.2.6 随机数法

2.2.7 总结

三、处理哈希冲突的方法

3.1 开放定址法

3.1.1 线性探测法

3.1.2 二次探测法

3.1.3 随机探测法

 3.1.4 总结

3.2 再哈希法

3.3 链地址法

3.3.1  链址法的插入元素操作

3.3.2  链址法的查找元素操作

3.3.4  链址法的删除元素操作

3.3.5  代码实现增删改查操作

3.3.6 总结 

3.4 公共溢出区法

四、一致性哈希+虚拟节点+布隆过滤器


一、什么是哈希表

1.0 为什么会有哈希表?

       之前学习的这么多抽象数据结构类型,例如顺序表,单链表..., 他们有一个共同的特点:
数据的存储地址和数据本身是没任何关系的,它是随机的,在这些结构中进行查找,因为不知道key(关键字)和记录在结构中存储位置的关系,所以只能让key和结构中的记录进行一一比较,但是不一定是每个记录都要与key进行比较。在顺序查找时,比较的结果有"=“和"≠"两种可能。在二分查找,比较的结果有”>“,”<“和”="三种可能。所以对于这种比较型查找,效率和比较的次数强关联,比较次数少,效率就高,比较次数多,效率就低。
       对于上诉所说的情况,有没有一种可能,我们不需要通过任何比较就能找到我们想要查询的记录,当然是有可能的。只需要在记录的存储位置和它的key建立一个确定的对应关系f(也可以称之为映射关系),使key与结构中唯一一个存储位置相对应。说到这里,初学者可能看得不是很明白,那就举个栗子:例如我在给别人自我介绍的时候,我说我来自重庆,此时在其他人脑海里就会联想到重庆火锅,小面等许多美食或者洪崖洞等地标性建筑。这就是一种映射关系,通过重庆这个关键字就能得到所对应的信息。

      因此在查找时,只需要通过这个对应关系,就能找到key所对应的值。如果结构中存在key和我们所想要查找的K相等的记录,则必定在f(K)的存储位置上,所以我们并不需要任何比较就能找到所查记录。对于这种映射关系,我们将其称之为哈希函数或者散列函数,按照这个思想建立的表,我们称之为哈希表或者散列表。

1.1 哈希表的基本概念

       哈希表(Hash table,也叫散列表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过键 key 和映射函数 Hash(key) 计算出对应的值 value,把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做哈希函数(散列函数),存放记录的数组叫做哈希表(散列表)。

         通过上面的概念,那样我们可以通过查找关键字不需要比较就可获得需要的记录的存储位置。这就是一种新的存储技术 —>散列技术

          散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f (key)。查找时,根据这个确定的对应关系找到给定值key的映射f (key), 若查找集合中存在这个记录,则必定在f (key) 的位置上。这里我们把这种对应关系f称为散列函数,又称为哈希(Hash) 函数。按这个思想,采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表(Hash table)。那么关键字对应的记录存储位置我们称为散列地址

1.2 基本思想

        哈希表的关键思想是使用哈希函数,将键 key 映射到对应表的某个区块中。

我们可以将算法思想分为两个部分:

1.  向哈希表中插入一个关键码值:哈希函数决定该关键字的对应值应该存放到表中的哪个区块,并将对应值存放到该区块中。

2.  在哈希表中查找一个关键码值:使用相同的哈希函数从哈希表中查找对应的区块,并在特定的区块搜索该关键字对应的值。

      因此,从上面理解,散列技术既是一种存储方法,也是一种查找方法。 然而它与线性表、树、图等结构不同的是,前面几种结构,数据元素之间都存在某种逻辑关系,可以用连线图示表示出来,而散列技术的记录之间不存在什么逻辑关系,它只与关键字有关联。因此,散列主要是面向查找的存储结构。散列技术最适合的求解问题是查找与给定值相等的记录。对于查找来说,简化了
比较过程,效率就会大大提高。但万事有利就有弊,散列技术不具备很多常规数据结构的能力。

1.3 举例理解

        下面要将一组数利用散列技术存储起来,那么应该如何存储?通过上面我们可以知道,哈希表的关键在于构造合适的哈希函数,这里我们将哈希函数构造为:数据本身对10取余,那么必然会得到0-9之间的数,得到的这个数其实就是将这个数据存放在数组/哈希表中的地址(也就是数组的下标)

      上面讲了如何存储,那么,我又应该如何去查找一个数到底在不在这个数组呢?如果在,又该如何返回这个数的位置呢?(下标)传统的数组查找方法,当然是:从头开始对比遍历整个数组,比较数据是否与我们要找的数相等,如果相等返回下标,很明显,它的时间复杂度比较高为:O(n),那么哈系表又是如何解决这个问题的呢?对于我们待查找的数据,同样通过哈希函数映射到存储的位置,然后直接在该位置查找是否存在该数据,如果该位置存在,代表找到了,如果没有找到,那它就是不存在,其他位置也不用继续找了,它是通过缩小查找范围,从而降低时间复杂度,相比遍历,大大的提高了效率。

1.4 存在的问题

      对于查找来说,简化了比较过程,效率就会大大提高。但万事有利就有弊,那就是哈希冲突。继续通过上面的例子来理解:上边的数据比较巧,一个格子刚好放一个值,那接下开看下面这组数据:

哈希冲突:理想情况下,每一个关键字通过哈希函数计算出来的存储位置都是不同的,但是这种想法不现实。实际使用中,经常会出现,两个或多个关键字不同,但是经过哈希函数计算得到的存储地址是一样的,也就是说Key1!=Key2,但是F(Key1) == F(Key2), 这种现象就是发生了哈希冲突,key1和key2 也叫做哈希函数的同义词。

       像上面的那个例子,22和32对10取余后结果都是2,代表他们二者都要存放在2的下标位置,但是下标为2的位置已经被22占了,这便产生了哈希冲突,从数学上简单理解:对于数学上的函数,都是自变量通过对应法则映射为函数值,它们是一对一的关系,但是这里便让它出现了多对一的关系,这里称作哈希冲突!生活上理解就是:两个人同时抢占一个位置,但是前面那个人已经把座位占了。

1.5 总结

       一般情况下,哈希冲突只能可能的减少,无法达到完全避免的效果。因为,哈希函数是从关键字集合到地址集合的映像。表越长,数据量越少,冲突的概率就越低,表越短,数据量越大,冲突的概率就越高。在建立哈希表时不仅要设定一个好的哈希函数,也要设定一种好的处理冲突的方法。

       综上所述,可如下描述哈希表:根据哈希函数和处理哈希冲突的方法,将一组记录的关键字映射到一个有限且连续的一段区间内,并以关键字处理得到地址作为记录在表中的存储位置,这种表就成为哈希表,这个过程称为哈希造表或散列,所得的存储位置称为哈希地址或者散列地址。

二、哈希函数的构造方法

2.1 哈希函数的理解

     从上面我们知道,哈希函数(Hash Function):将哈希表中元素的关键键值映射为元素存储位置的函数。 构成哈希表的关键在于:哈希函数/散列函数的构造;在哈希表的实际应用中,关键字的类型除了数字类,还有可能是字符串类型、浮点数类型、大整数类型,甚至还有可能是几种类型的组合。一般我们会将各种类型的关键字先转换为整数类型,再通过哈希函数,将其映射到哈希表中。如果关键字是字符串如何处理?其实无论是英文字符,还是中文字符,也包括各种各样的符号,它们都可以转化为某种数字来对待,比如ASCII 码或者Unicode码等,因此也就可以使用上面的这些方法。

一个哈希函数的好不好,取决于以下三点

  • 哈希函数的定义域必须包括需要存储的全部关键码,而如果哈希表允许有m个地址时,其值域必须在0 到m-1之间
  • 哈希函数计算出来的地址能均匀分布在整个空间中,刚才提到冲突带来的问题,最好的办法就是尽量让散列地址均匀地分布在存储空间中,这样可以保证存储空间的有效利用,并减少为处理冲突而耗费的时间。
  • 哈希函数应该比较简单

2.2构造方法

      接下来我们就要介绍几种常用的散列函数构造方法。估计设计这些方法的前辈们当年可能是从事间谍工作,因为这些方法都是将原来数字按某种规律变成另一个数字而已。构造哈希函数的方法有很多,但能尽可能的避免哈希冲突才能称得上好的哈希函数。换言之,就是关键字通过哈希函数的处理得到一个随机的地址,以便使一组关键字的哈希地址均匀的分布在整个地址区间,从而减少哈希冲突。


2.2.1 直接定址法

        取关键字或关键字的某个线性函数值为哈希函数,即:H(key) = key或者H(key) = a·key+b,其中a和b是常数。

优点:简单、均匀,也不会产生冲突;

缺点:需要事先知道关键字的分布情况,适合查找表较小且连续的情况。

        由于这样的限制,在现实应用中,此方法虽然简单,但却并不常用。

       适用场景:适合查找较小数据范围且连续的情况

   

2.2.2 数字分析法

     假设关键字是一个N进制数, 并且哈希表中的可能出现的关键字都是事先知道的,则可以去关键字的若干数位组合成哈希地址。

       数字分析法通常适合处理关键字位数比较大的情况,如果事先知道关键字的分布且关键字的若干位分布较均匀,就可以考虑用这个方法。

2.2.3 平方取中法

    如果关键字的每一位都有某些数字重复出现频率很高的现象,可以先求关键字的平方值,通过平方扩大差异,而后取中间数位作为最终存储地址。

适用场景:事先不知道数据并且数据长度较小的情况

2.2.4 折叠法

折叠法事先不需要知道关键字的分布,适合关键字位数较多的情况。

2.2.5 除留余数法(最常用)

      取关键字被某个不大于哈希表表长m的数p除后所得的余数为哈希地址。即:H(key) = key % p,p ≤ m。这是一种简单,也是最常用的构造哈希函数的方法。它不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。这也是一种简单且常用的哈希函数方法。其关键点在于 p 的选择。根据经验而言,一般 p 取素数或者 m,这样可以尽可能的减少冲突。

2.2.6 随机数法

选择一个随机函数,取关键字的随机函数值为它的哈希地址,即:H(key) = random(key),其中random为随机函数。通常,当关键字长度不等时采用此方法构造哈希函数比较恰当。

2.2.7 总结

      总之,现实中,应该视不同的情况采用不同的哈希函数。我们只能给出一些考虑的因素来提供参考:

  1. 计算哈希函数所需要的时间(包括硬件指令的因素)
  2. 关键字的长度
  3. 哈希表的大小
  4. 关键字的分布情况
  5. 记录的查找频率

三、处理哈希冲突的方法

       在前面哈希表的概念中提及到好的哈希函数能够减少哈希冲突,那我们肯定会不可避免地发生哈希冲突,那又该如何解决呢?

3.1 开放定址法

      所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址只要散列表足够大,空的散列地址总能找到,并将记录存入。总之,开放定址法只要在散列表未填满时,总是能找到不发生冲突的地址,是我们常用的解决冲突的办法。

         Hi = H(key)+di)%m i = 1, 2, 3…,k(k ≤ m-1)其中:H(key)为哈希函数,m为哈希表表长,d为增量序列,可以有下列3种取法:

  1. di = 1, 2, 3, …, m - 1,称为线性探测法
  2. di = 1², 2², 3², …, k² (k ≤ m/2),称为二次探测法
  3. di = 伪随机数序列,称为随机探测法

3.1.1 线性探测法

       例如,在长度为10的哈希表中已经填入有关键字分别为35、56和97的记录(哈希函数H(key) = key % 10),现在有第四个记录,其关键字为65,有哈希函数得到哈希地址为5,产生冲突。若采取线性探测再散列,得到下一个地址6,让然冲突,再求下一个地址,还是冲突的,知道哈希地址为8,不冲突后将该记录放入,处理哈希冲突的过程也就结束。

3.1.2 二次探测法

       从上述线性探测再散列的过程中我们可以发现一个现象:当表中i,i+1,i+2位置已经被填上的时候,当下次插入数据的哈希地址为i,i+1,i+2和i+3时都会填入到i+3的位置,这种在处理冲突过程中发生的第一个哈希地址不同的记录去争夺同一个后继哈希地址的现象称为“二次聚集”即在处理同义词(哈希地址相同的记录)的冲突中有添加了非同义词的冲突,显然这种对查找不利。


       举个栗子:比如在一张表中,表的大小为150,如果前面100个地址所对应的空间都填入了记录,现在需要插入一个哈希地址为1的记录,经过线性探测再散列的方式,最终会将这个记录填入到哈希地址为101的空间。如果在查找这个记录时,就需要从表的起始位置开始查找,直到哈希地址为101为止。本来我们是想通过哈希表进行1次查找就能找到记录,但实际上却查找了101次,效率大大得降低了。
      相对于线性探测法的方式,二次线性探测法能降低二次聚集,大家可以思考一下为什么。另外增加平方运算的目的是为了不让关键字都聚集在某一块区域。我们称这种方法为二次探测法。
但在另一方面,用线性探测法处理冲突可以保证做到:只要哈希表未被填满,总能找到一个不发生哈希冲突的地址。而二次探测法只有哈希表长为m形如4j+3(j为整数)的素数是才有可能。

3.1.3 随机探测法

在冲突时,对于位移量d; 采用随机函数计算得到,我们称之为随机探测法。

 3.1.4 总结

         二次探测和线性探测都属于闭散列,其原理都一样,两者的主要区别就是探测的方式不同,线性探测是如果要插入的位置已有元素,会一个一个往后查找到新的空位置。而二次探测是通过该位置的哈希冲突次数的平方来向后查找新的位置将产生哈希冲突的数据分散,使不堆积在一起。但是这两种方法都有很大的缺陷,就是空间利用率低。在这个基础上,引进一种新的技术来解决哈希冲突----链地址法(开散列)

3.2 再哈希法

        每当发生散列地址冲突时,就换一个散列函数计算,相信总会有一个可以把冲突解决掉。这种方法能够使得关键字不产生聚集,当然,相应地也增加了计算的时间。

3.3 链地址法

       思路再换一换,为什么有冲突就要换地方呢,我们直接就在原地想办法不可以吗?于是我们就有了链地址法。

链地址法(Chaining):将具有相同哈希地址的元素(或记录)存储在同一个线性链表中。链地址法是一种更加常用的哈希冲突解决方法。相比于开放地址法,链地址法更加简单。

       哈希表中的每个元素都是一个指向有效结点的指针,存储的是链表的第一个有效结点,特点:具有相同的哈希地址会存放在同一链表中,每个链表中的元素都具有相同的哈希地址。

结构如下:

      该哈希表示由指针数组来组成的,每个数组中的元素都是一个链表的头指针。从该表中我们可以看出,产生哈希冲突的元素并不会占用其他元素的位置,每个链表中的元素都是哈希冲突的元素。

3.3.1  链址法的插入元素操作

        当我们插入时,计算出哈希地址,就可以直接定位到该key对应的链表的头结点,但是由于不能存放相同的key,我们需要遍历该链表中是否存在相同元素,如果不存在才进行插入。插入时我们可以进行头插或者尾插,这里采用头插法时间复杂度更低。

3.3.2  链址法的查找元素操作

      查找:查找一个元素,我们可以先计算出要查找元素的哈希地址,直接定位到指定的链表的头结点,然后遍历该条链表,如果当前节点的值和要查找的元素的值相同,则表示查找,返回所找到的结点,如果没有找到则返回空

3.3.4  链址法的删除元素操作

      可以先通过查找,查看要删除的值是否存在哈希表中,如果不存直接返回false,存在则需要先计算当前删除的结点的所在链表的哈希地址,找到后遍历该链表,并用一个prev结点记录要删除的前一个结点,当遍历找到我们删除的结点时,要先判断该节点是否为链表的头结点,如果为头结点,则将要删除的结点的下一个结点置为头结点,如果不是头结点则将记录的前结点prev的下一个结点的next置为要删除结点的下一个结点。最后将有效元素-1并删除要删除的结点.

3.3.5  代码实现增删改查操作

哈希表的设计:首先它是一个数组,数组的每个元素都是一个由带头结点串起来的单链表。

#define MAX_SIZE 12

//1.有效结点
typedef struct Node
{
	ELEM_TYPE data;
	struct Node* next;
}Node,*PNode;


//结点数组(数组的每一个元素是结点类型,是单链表的头结点)
typedef struct List_Hash
{
	struct Node arr[MAX_SIZE];   //该数组一共12个元素,每一个元素都是一个头结点
}List_Hash,*PList_Hash;

实现的主要函数

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "Hash.h"

//初始化
void Init_List_Hash(struct List_Hash * lh);

//插入
bool Insert_List_Hash(struct List_Hash * lh, ELEM_TYPE val);

//删除
bool Del_List_Hash(struct List_Hash * lh, ELEM_TYPE val);

//查找
struct Node* Search_List_Hash(struct List_Hash *lh, ELEM_TYPE val);

//打印
void Show(struct List_Hash * lh);

具体实现: 


//初始化
void Init_List_Hash(struct List_Hash * lh)
{
	for(int i=0; i<12; i++)
	{
		lh->arr[i].next = NULL;
	}
}

//插入
bool Insert_List_Hash(struct List_Hash * lh, ELEM_TYPE val)
{
	//1.购买新节点
	struct Node * pnewnode = (struct Node *)malloc(1 * sizeof(struct Node));
	pnewnode->val = val;

	//2.找到合适的插入位置
	int index = val % MAX_SIZE;

	//3.插入
	pnewnode->next = lh->arr[index].next;
	lh->arr[index].next = pnewnode;

	return true;
}

//删除
bool Del_List_Hash(struct List_Hash * lh, ELEM_TYPE val)
{
	//1.先通过哈希函数判断在哪一个下标对应头结点后边
	int index = val % MAX_SIZE;

	//2.在这个头结点后面进行遍历,找val值节点是否存在,直接用p指向
	struct Node *p = Search_List_Hash(lh, val);
	if(p == NULL)
	{
		return false;
	}

	//3.让指针q,指向待删除节点的上一个节点
	struct Node *q = &lh->arr[index];
	for( ; q->next != p; q=q->next);

	//4.跨越指向+释放
	q->next = p->next;
	free(p);
	p = NULL;

	return true;
}

//查找
struct Node* Search_List_Hash(struct List_Hash *lh, ELEM_TYPE val)
{
	int index = val % MAX_SIZE;
	struct Node *p = lh->arr[index].next;

	for(; p!=NULL; p->next)
	{
		if(p->val == val)
		{
			return p;
		}
	}

	return NULL;
}

//打印
void Show(struct List_Hash * lh)
{
	for(int i=0; i<MAX_SIZE; i++)
	{
		printf("%d : ", i);

		struct Node *p = lh->arr[i].next;
		for(; p!=NULL; p=p->next)
		{
			printf("-> %d ", p->val);
		}

		printf("\n");
	}


}

 功能测试:

int main()
{
	struct List_Hash BigHead;

	Init_List_Hash(&BigHead);

	Insert_List_Hash(&BigHead, 67);
	Insert_List_Hash(&BigHead, 15);
	Insert_List_Hash(&BigHead, 29);
	Insert_List_Hash(&BigHead, 16);
	Insert_List_Hash(&BigHead, 12);
	Insert_List_Hash(&BigHead, 22);
	Insert_List_Hash(&BigHead, 25);
	Insert_List_Hash(&BigHead, 34);
	Insert_List_Hash(&BigHead, 47);
	Insert_List_Hash(&BigHead, 48);
	Insert_List_Hash(&BigHead, 37);
	Insert_List_Hash(&BigHead, 56);

	Show(&BigHead);


}

 结果展示: 

3.3.6 总结 

总结:

     相对于开放地址法,采用链地址法处理冲突要多占用一些存储空间(主要是链节点占用空间)。但它可以减少在进行插入和查找具有相同哈希地址的关键字的操作过程中的平均查找长度。这是因为在链地址法中,待比较的关键字都是具有相同哈希地址的元素,而在开放地址法中,待比较的关键字不仅包含具有相同哈希地址的元素,而且还包含哈希地址不相同的元素。 

3.4 公共溢出区法

四、一致性哈希+虚拟节点+布隆过滤器

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

libcity笔记:参数设置与参数优先级

1 参数优先级 高优先级的参数会覆盖低优先级的同名参数 Libcity中的优先级顺序维&#xff1a; 命令行参数&#xff08;命令行python run_model.py时导入的&#xff09; > 用户定义配置文件&#xff08;命令行python run_model.py时由config_file导入的&#xff09; >…

javascript 练习 写一个简单 另类录入 电脑组装报价表 可打印

数据格式 &#xff08;1代表cpu、2代表主板、3代表内存、。。。&#xff09; 1i3 12100 630 2H610 480 3DDR4 3200 16G 220 4500G M.2 299 5300W电源 150 6小机箱 85 7GT 730G 4G 350 8WD 2T 399 9飞利浦 24Led 580 主代码 Html JS <!DOCTYPE html> <html lang&qu…

02_Java综述

目录 面向对象编程两种范式抽象OOP 三原则封装继承多态多态、封装与继承协同工作 面向对象编程 面向对象编程(Object-Oriented Programming&#xff0c;OOP)在Java中核心地位。几乎所有的Java程序至少在某种程度上都是面向对象的。OOP与java是密不可分的。下面说一下OOP的理论…

SSM+Vue酒店管理系统

SSMVue酒店管理系统&#xff0c;JavaWeb酒店管理系统&#xff0c;项目由maven工具管理依赖&#xff0c;数据库Mysql&#xff0c;一共19张表&#xff0c;前端用Vue写的管理端&#xff0c;功能丰富&#xff0c;需要可在最后位置联系我&#xff0c;可加购调试&#xff0c;讲解&…

自注意力架构大成者_Transformer(Pytorch 17)

1 模型简介 在上节比较了 卷积神经网络&#xff08;CNN&#xff09;、循环神经网络&#xff08;RNN&#xff09;和 自注意力&#xff08;self‐attention&#xff09;。值得注意的是&#xff0c; 自注意力同时具有并行计算和最短的最大路径长度这两个优势。因此&#xff0c;使…

Llama3本地部署与高效微调入门

前言 为了保持公司在AI&#xff08;人工智能&#xff09;开源大模型领域的地位&#xff0c;社交巨头Meta推出了旗下最新开源模型。当地时间4月18日&#xff0c;Meta在官网上宣布公布了旗下最新大模型Llama 3。目前&#xff0c;Llama 3已经开放了80亿&#xff08;8B&#xff09…

8086 汇编学习 Part 9

端口的读写 CPU 的邻居 CPU 内部的寄存器内存单元端口&#xff08;各种接口卡、网卡&#xff0c;显卡&#xff0c;主板上的接口芯片等&#xff09; 各种芯片工作时&#xff0c;都有一些寄存器由 CPU 读写从 CPU 角度&#xff0c;将各寄存器当端口&#xff0c;并统一编制CPU …

开源im即时通讯app源码系统/php即时聊天im源码/php+uniapp框架【终身使用】

摘要 随着开源文化的蓬勃发展&#xff0c;即时通讯(IM)系统作为现代通信不可或缺的一部分&#xff0c;其开源实现正变得越来越普遍。本文将深入探讨基于PHP的全开源即时通讯源码系统&#xff0c;并结合UniApp开源框架&#xff0c;从理论基础到代码实现&#xff0c;再到实际应用…

探索设计模式的魅力:分布式模式让业务更高效、更安全、更稳定

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 ✨欢迎加入探索分布式模式之旅✨ 在数字化时代&#xff0c;企业面临着前所未有的挑战和机遇。…

机器学习-K近邻算法(KNN)

目录 什么是KNN算法 图解KNN基本算法 &#xff08;1&#xff09;k近邻算法中k的选取 &#xff08;2&#xff09;距离函数 &#xff08;3&#xff09;归一化处理 &#xff08;4&#xff09;概率kNN KNN算法的优缺点 优势 缺点 KNN算法总结 什么是KNN算法 k近邻算法&…

vs 2022 Xamarin 生成 Android apk

再保存&#xff0c;如果没有生成apk就重启软件 再试一次

(论文阅读-优化器)Volcano-An Extensible and Parallel Query Evaluation System

目录 摘要 一、简介 三、火山模型系统设计 3.1 文件系统 3.2 查询处理 四、扩展性 五、动态查询评估计划 六、多处理器查询评估 6.1 垂直并行化 6.2 水平并行化Horizontal 6.3 exchange operator的变体 6.4 文件系统修改 七、总结 摘要 火山模型Volcano在数据库查…

详解LLMOps,将DevOps用于大语言模型开发

大家好&#xff0c;在机器学习领域&#xff0c;随着技术的不断发展&#xff0c;将大型语言模型&#xff08;LLMs&#xff09;集成到商业产品中已成为一种趋势&#xff0c;同时也带来了许多挑战。为了有效应对这些挑战&#xff0c;数据科学家们转向了一种新型的DevOps实践LLM-OP…

Maven 在项目的 pom.xml 文件中 指定 阿里云的景象仓库

配置 在 项目的 pom.xml 文件中添加如下配置即可 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation&…

Android NDK开发——Android Studio 3.5.2安装与配置踩坑

Android NDK开发——Android Studio 3.5.2安装与配置踩坑 一、Android Studio下载二、配置踩坑报错1&#xff1a;Failed to install the following Android SDK packages as some licences have not been accepted报错2&#xff1a;No toolchains found in the NDK toolchains …

如何修复连接失败出现的错误651?这里提供修复方法

错误651消息在Windows 7到Windows 11上很常见&#xff0c;通常会出现在一个小的弹出窗口中。实际文本略有不同&#xff0c;具体取决于连接问题的原因&#xff0c;但始终包括文本“错误651”。 虽然很烦人&#xff0c;但错误651是一个相对较小的问题&#xff0c;不应该导致计算…

UI组件库和内容文字的中英文切换

同时实现UI组件库(这里以ElementPlus为例)和内容文字的中英文切换 1. 安装vueI18n和element-plus pnpm i vue-i18n element-plus 2. 然后在项目中src目录下新建lang文件夹&#xff0c;里面新建en.ts和zh.ts还有index.ts index.ts import { createI18n } from vue-i18n impor…

【Android】Android应用性能优化总结

AndroidApp应用性能优化总结 最近大半年的时间里&#xff0c;大部分投在了某国内新能源汽车的某款AndroidApp开发上。 由于该App是该款车上&#xff0c;常用重点应用。所以车厂对应用性能的要求比较高。 主要包括&#xff1a; 应用冷启动达到***ms。应用热(温)启动达到***ms应…

【备战软考(嵌入式系统设计师)】07 - 计算机网络模型

七层模型 计算机网络中比较常见的有OSI七层模型和TCP/IP四层模型。 软考中主要考七层模型&#xff0c;但是实际中使用的还是四层模型比较多&#xff0c;我们主要是为了考试&#xff0c;那就主要讲讲七层模型。不过实际上四层模型就是将七层模型压缩了三层&#xff0c;本质上是…

Nginx(参数设置总结)

文章目录 Nginx&#xff08;工作机制&参数设置&#xff09;1.Master&Worker工作机制1.示意图2.解释3.Nginx争抢机制4.accept_mutex解决惊群现象5.多进程结构不用多线程结构的好处6.IO多路复用&#xff0c;实现高并发7.优势 2.参数配置1.work_processes1.基本介绍2.work…