数据结构十:哈希表

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

目录

一、什么是哈希表

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.mzph.cn/diannao/6788.shtml

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

相关文章

基于JSP的人才公寓管理系统

目录 背景 技术简介 系统简介 界面预览 背景 随着互联网的广泛推广和应用&#xff0c;人才公寓管理系统在网络技术的推动下迅速进步。该系统的设计初衷是满足住户的实际需求&#xff0c;通过深入了解住户的期望&#xff0c;开发出高度定制化的人才公寓管理系统。利用互联网…

Django关于ORM的增删改查

Django中使用orm进行数据库的管理&#xff0c;主要包括以下步骤 1、创建model&#xff0c; 2、进行迁移 3、在视图函数中使用 以下的内容可以先从查询开始看&#xff0c;这样更容易理解后面删除部分代码 主要包括几下几种&#xff1a; 1、增 1&#xff09;实例例化model,代…

SpringTask定时任务

SpringBoot项目定时任务 首先在启动类引入注解EnableScheduling然后在方法中加注解Scheduled(cron“”)cron表达式 生成cron https://www.pppet.net/

流畅的Python阅读笔记

五一快乐的时光总是飞快了&#xff0c;不知多久没有拿起键盘写文章了&#xff0c;最近公司有Python的需求&#xff0c;想着复习下Python吧&#xff0c;然后就买了本Python的书籍 书名&#xff1a; 《流畅的Python》 下面是整理的一个阅读笔记&#xff0c;大家自行查阅&#xf…

【Qt QML】Frame组件

Frame&#xff08;框架&#xff09;包含在&#xff1a; import QtQuick.Controls继承自Pane控件。用于在可视框架内布局一组逻辑控件。简单来说就是用来包裹和突出显示其他可视元素。Frame不提供自己的布局&#xff0c;但需要自己对元素位置进行设置和定位&#xff0c;例如通过…

Numerical Analysis(byRichard.L..Burden)【pdf高清英文原版】

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

基于TL431基准电压源的可调恒压恒流源的Multisim电路仿真设计

1、线性电源的工作原理 在我们日常应用里&#xff0c;直流电是从市电或电网中的交流电获取的。例如15V直流电压源、24V直流电压源等等。交流电变为直流电的过程大概分为一下几步&#xff1a; 首先&#xff0c;交流电通过变压器降低其电压幅值。接着&#xff0c;经过整流电路进…

基于机器学习的网络流量识别分类

1.cicflowmeter的目录框架&#xff1a; 各部分具体代码 FlowMgr类&#xff1a; package cic.cs.unb.ca.flow;import cic.cs.unb.ca.Sys; import org.slf4j.Logger; import org.slf4j.LoggerFactory;import java.time.LocalDate;public class FlowMgr {protected static final…

项目管理【人】概述

系列文章目录 【引论一】项目管理的意义 【引论二】项目管理的逻辑 【环境】概述 【环境】原则 【环境】任务 【环境】绩效 【人】概述 一、项目涉及到的人 1.1 项目发起人、项目指导委员会和变更控制委员会 项目发起人&#xff08;Sponsor&#xff09; 项目指导委员会&…

JVM笔记4-虚拟机类加载机制

1、概述 Java虚拟机把描述类的数据从Class文件加载到内存中&#xff0c;并对数据进行检验、转换解析和初始化&#xff0c;最终形成可以被虚拟机直接使用的Java类型。这个过程称为虚拟机的类加载机制。 2、类加载的时机 一个类型从被加载到内存中开始&#xff0c;到卸载出内存…

使用socket+Python实现ping

import os import socket import struct import select import time# 计算校验和&#xff0c;用于确保数据的完整性 def checksum(source_string):sum 0count 0max_count len(source_string)# 处理成对的字节while count < max_count - 1:val source_string[count 1] *…

5.5代码

目录 1.内存空间 1.内存空间 真的要吐了&#xff0c;人都麻了题还没看完&#xff0c;看样子就是要想办法提取出来想要的东西2022第十三届蓝桥杯决赛C/C大学A组-C题内存空间_蓝桥杯a组c语言题目-CSDN博客 这个是一个非常清晰的代码&#xff0c;好几个帖子都管这个题叫大模拟题…

开源版本管理系统的搭建二:SVN部署及使用

作者&#xff1a;私语茶馆 1. Visual SVN Server部署 SVN Server部署包括&#xff1a; 创建版本仓库创建用户 这些部署是通过VisualSVN Server Manager实现的&#xff0c;如下图&#xff1a; VisualSVN Server Manager&#xff08;安装后自带&#xff09; 1.1.SVN 初始化配…

ROS2学习——Docker环境下安装于使用(1)

目录 一、简要 二、ROS2和ROS1区别 三、环境搭建与安装 &#xff08;2&#xff09;拉取ubuntu22.04镜像 &#xff08;2&#xff09;安装ROS2 1. 基本设置 2.设置源 3.安装ROS2功能包 4.测试 四、相关指令学习 1.小海龟测试 2.ros2 node等指令 3.rqt 一、简要 随着R…

Git常用(持续更新)

常用场景&#xff1a; 初始化&#xff1a; git config --global user.name "codelabs" git config --global user.email mycodelabs.com git init git remote add origin https://github.com/username/repository.git git pull origin master 提交&#xff1a; gi…

踏春正当时!VELO Prevail Ride带你探索多元骑行潮流体验~

嘿&#xff0c;朋友&#xff01;踏春正当时嘞&#xff01;在这个追求个性化与多元化的新时代&#xff0c;骑行爱好者们也开始寻找能适应各种骑行场景的理想坐垫。从悠闲自在的日常通勤&#xff0c;到热血沸腾的公路竞速&#xff0c;再到勇攀高峰的山地探险&#xff0c;维乐VELO…

HNU-人工智能-实验3-贝叶斯分类器

人工智能-实验3 计科210x 甘晴void 【感悟】本实验值得自己完成一遍 文章目录 人工智能-实验3一、实验目的二、实验平台三、实验内容3.0 基础知识3.1 条件概率&#xff08;选择题&#xff09;3.2 贝叶斯公式&#xff08;选择题&#xff09;3.3 朴素贝叶斯分类算法流程3.3.1 算…

多线程系列(三) -synchronized 关键字使用详解

一、简介 在之前的线程系列文章中&#xff0c;我们介绍了线程创建的几种方式以及常用的方法介绍。 今天我们接着聊聊多线程线程安全的问题&#xff0c;以及解决办法。 实际上&#xff0c;在多线程环境中&#xff0c;难免会出现多个线程对一个对象的实例变量进行同时访问和操…

3-qt综合实例-贪吃蛇的游戏程序

引言&#xff1a; 如题&#xff0c;本次实践课程主要讲解贪吃蛇游戏程序。 qt贪吃蛇项目内容&#xff1a; 一、功能需求 二、界面设计 各组件使用&#xff1a; 对象名 类 说明 Widget QWidge 主窗体 btnRank QPushButton 排行榜-按钮 groupBox QGroupBox 难…

python数据分析——数据分析的统计推断

数据分析的统计推断 前言一、提出问题二、统计归纳方法三、统计推断四、统计推断步骤4.1.点估计4.2.区间估计4.2.1. 总体方差已知4.2.2总体方差未知 4.3. 假设检验4.4. 假设检验的假设4.5.显著性水平 五、检验统计量六、检验方法七、拒绝域八、假设检验步骤九、重要假设检验方法…