【数据结构(C++版)】哈希表(散列表)

目录 

1. 散列表的概念

2. 散列函数的构造方法

2.1 直接定址法

2.2 除留余数法

2.3 数字分析法

2.4 平方取中法

3. 处理冲突的方法

3.1 开放定址法

3.1.1 线性探测法

3.1.2 平方探测法

3.1.3 双散列法

3.1.4 伪随机序列法

3.2 拉链法(链接法)

4. 散列查找及性能分析

5. 哈希的应用

5.1 位图

5.1.1 位图的概念

5.1.2 位图的实现

5.1.3 位图的应用

5.2 布隆过滤器

5.2.1 布隆过滤器的提出

5.2.2 布隆过滤器的概念

5.2.3 布隆过滤器的实现

5.2.4 布隆过滤器的应用


1. 散列表的概念

在线性表和树表的查找中,记录在表中的位置与记录的关键字之间不存在确定关系,因此,在这些表中查找记录时需进行一系列的关键字比较。这类查找方法建立在“比较”的基础上,查找的效率取决于比较的次数。

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

散列函数可能会把两个或两个以上的不同关键字映射到同一地址,称这种情况为冲突,这些发生碰撞的不同关键字称为同义词。一方面,设计得好的散列函数应尽量减少这样的冲突;另一方面,由于这样的冲突总是不可避免的,所以还要设计好处理冲突的方法。

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

理想情况下,对散列表进行查找的时间复杂度为O(1),即与表中元素的个数无关。下面分别介绍常用的散列函数和处理冲突的方法。

2. 散列函数的构造方法

在构造散列函数时,必须注意以下几点:

  • 散列函数的定义域必须包含全部需要存储的关键字,而值域的范围则依赖于散列表的大小或地址范围。
  • 散列函数计算出来的地址应该能等概率、均匀地分布在整个地址空间中,从而减少冲突的发生。
  • 散列函数应尽量简单,能够在较短的时间内计算出任意一个关键字对应的散列地址。下面介绍常用的散列函数。

2.1 直接定址法

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

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

式中,a和b是常数。这种方法计算最简单,且不会产生冲突。它适合关键字的分布基本连续的情况,若关键字分布不连续,空位较多,则会造成存储空间的浪费。

2.2 除留余数法

这是一种最简单、最常用的方法,假定散列表表长为m,取一个不大于m但最接近或等于m的质数p,利用以下公式把关键字转换成散列地址。散列函数为

H(key) = key % p

除留余数法的关键是选好p,使得每个关键字通过该函数转换后等概率地映射到散列空间上的任意一个地址,从而尽可能减少冲突的可能性。

2.3 数字分析法

设关键字是r进制数(如十进制数),而r个数码在各位上出现的频率不一定相同,可能在某些位上分布均匀一些,每种数码出现的机会均等:而在某些位上分布不均匀,只有某几种数码经常出现,此时应选取数码分布较为均匀的若干位作为散列地址。这种方法适合于已知的关键字集合,若更换了关键字,则需要重新构造新的散列函数。

2.4 平方取中法

顾名思义,这种方法取关键字的平方值的中间几位作为散列地址。具体取多少位要视实际情况而定。这种方法得到的散列地址与关键字的每位都有关系,因此使得散列地址分布比较均匀,适用于关键字的每位取值都不够均匀或均小于散列地址所需的位数。

在不同的情况下,不同的散列函数具有不同的性能,因此不能笼统地说哪种散列函数最好。在实际选择中,采用何种构造散列函数的方法取决于关键字集合的情况,但目标是尽量降低产生冲突的可能性。

3. 处理冲突的方法

应该注意到,任何设计出来的散列函数都不可能绝对地避免冲突。为此,必须考虑在发生冲突时应该如何处理,即为产生冲突的关键字寻找下一个“空”的Hash地址。用Hi表示处理冲突中第i次探测得到的散列地址,假设得到的另一个散列地址H1仍然发生冲突,只得继续求下一个地址H2,以此类推,直到Hk不发生冲突为止,则Hk为关键字在表中的地址。

3.1 开放定址法

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

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

式中,H(key)为散列函数;i = 0,1,2,…,k(k≤m-1);m表示散列表表长;di为增量序列。

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

3.1.1 线性探测法

当di = 0,1,2,…,m-1时,称为线性探测法。这种方法的特点是:冲突发生时,顺序查看表中下一个单元(探测到表尾地址m-1时,下一个探测地址是表首地址0),直到找出一个空闲单元(当表未填满时一定能找到一个空闲单元)或查遍全表。线性探测法可能使第i个散列地址的同义词存入第i+1个散列地址,这样本应存入第i+1个散列地址的元素就争夺第i+2个散列地址的元素的地址……从而造成大量元素在相邻的散列地址上“聚集”(或堆积)起来,大大降低了查找效率。

3.1.2 平方探测法

当di = 0^{2},1^{2},-1^{2},2^{2},-2^{2},…,k^{2},-k^{2}时,称为平方探测法,其中k≤m/2,散列表长度m必须是一个可以表示成4k+3的素数,又称二次探测法。

平方探测法是一种处理冲突的较好方法,可以避免出现“堆积”问题,它的缺点是不能探测到散列表上的所有单元,但至少能探测到一半单元。

3.1.3 双散列法

当di = Hash_{2}(key)时,称为双散列法。需要使用两个散列函数,当通过第一个散列函数H(key)得到的地址发生冲突时,则利用第二个散列函数Hash_{2}(key)计算该关键字的地址增量。它的具体散列函数形式如下:

Hi = (H(key) + i*Hash_{2}(key)) % m

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

3.1.4 伪随机序列法

当di = 伪随机数序列时,称为伪随机序列法。

注意:在开放定址的情形下,不能随便物理删除表中的已有元素,因为若删除元素,则会截断其他具有相同散列地址的元素的查找地址。因此,要删除一个元素时,可给它做一个删除标记,进行逻辑删除。但这样做的副作用是:执行多次删除后,表面上看起来散列表很满,实际上有许多位置未利用,因此需要定期维护散列表,要把删除标记的元素物理删除。

3.2 拉链法(链接法)

显然,对于不同的关键字可能会通过散列函数映射到同一地址,为了避免非同义词发生冲突,可以把所有的同义词存储在一个线性链表中,这个线性链表由其散列地址唯一标识。假设散列地址为i的同义词链表的头指针存放在散列表的第i个单元中,因而查找、插入和删除操作主要在同义词链中进行。拉链法适用于经常进行插入和删除的情况。

例如,关键字序列为{19,14,23,01,68,20,84,27,55,11,10,79},散列函数H(key)=key % 13,用拉链法处理冲突,建立的表如图所示。

4. 散列查找及性能分析

散列表的查找过程与构造散列表的过程基本一致。对于一个给定的关键字key,根据散列函数可以计算出其散列地址,执行步骤如下:

初始化:Addr = Hash(key);

  1. 检测查找表中地址为Addr的位置上是否有记录,若无记录,返回查找失败;若有记录,比较它与key的值,若相等,则返回查找成功标志,否则执行步骤2。
  2. 用给定的处理冲突方法计算“下一个散列地址”,并把Addr置为此地址。转入步骤①。

例如,关键字序列{19,14,23,01,68,20,84,27,55,11,10,79}按散列函数H(key) = key % 13和线性探测处理冲突构造所得的散列表L如图所示。

给定值84的查找过程为:首先求得散列地址H(84)=6,因L[6]不空且L[6]≠84,则找第一次冲突处理后的地址H1=(6+1)%16=7,而L[7]不空且L[7]≠84,则找第二次冲突处理后的地址H2=(6+2)%16=8,L[8]不空且L[8]=84,查找成功,返回记录在表中的序号8。

给定值38的查找过程为:先求散列地址H(38)=12,L[12]不空且L[12]≠ 38,则找下一地址H1=(12+1)%16=13,由于L[13]是空记录,故表中不存在关键字为38的记录。

查找各关键字的比较次数如图所示。

平均查找长度ASL为

ASL = (1*6 + 2 + 3*3 + 4 + 9) / 12  = 2.5

对同一组关键字,设定相同的散列函数,则不同的处理冲突的方法得到的散列表不同,它们的平均查找长度也不同,本例与上节采用拉链法的平均查找长度不同。

从散列表的查找过程可见:

  1. 虽然散列表在关键字与记录的存储位置之间建立了直接映像,但由于“冲突”的产生,使得散列表的查找过程仍然是一个给定值和关键字进行比较的过程。因此,仍需要以平均查找长度作为衡量散列表的查找效率的度量。
  2. 散列表的查找效率取决于三个因素:散列函数、处理冲突的方法和装填因子。

装填因子。散列表的装填因子一般记为α,定义为一个表的装满程度,即

\alpha = \frac{n}{m}

n—表中记录数,m—散列表长度。

散列表的平均查找长度依赖于散列表的装填因子α,而不直接依赖于n或m。直观地看,α越大,表示装填的记录越“满”,发生冲突的可能性越大,反之发生冲突的可能性越小。

5. 哈希的应用

5.1 位图

5.1.1 位图的概念

给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。

解决方法:

  1. 遍历:O(N)
  2. 排序:O(NlogN) + 二分查找:O(logN)
  3. 位图

1KB = 1024Byte

1MB = 1024KB

1GB = 1024MB = 1024*1024*1024Byte = 10 7374 1824Byte ≈ 10亿Byte

40亿个整数占用40亿*4 = 160亿Byte,即16GB,将它们全部存储是不可能的,前两种方法不可行。

数据是否存在是两种状态,可以用一个比特位表示这种状态,1表示存在,0表示不存在。

位图就是哈希表直接定址法的变形。用位图表示{ 1,3,7,4,12,16,19,13,22,18 }中的元素是否存在:

上述数组中最大的元素是22,所以我们要表示0~22之间的元素是否存在,理论上只需要开辟23个bit即可,但是内存无法按bit开辟(除了位段),所以我们以Byte为单位开辟空间。

如何知道数据映射到哪里呢,以19为例:

  1. 先num/8,算出num映射到哪个char里。19/8=2,19在_bits[2]这个char中。
  2. 再num%8,算出num在这个char中的哪一位(从0开始,从后往前数)。19%8=3,19在_bits[2]的第3位。

5.1.2 位图的实现

template<size_t N>
class bitset
{
public://构造函数bitset(){_bits.resize(N / 8 + 1, 0);//N/8只保留整数部分,为防止空间不够,再+1}//将x映射的bit置1void set(size_t x){size_t i = x / 8;size_t j = x % 8;_bits[i] |= (1 << j);}//将x映射的bit置0void reset(size_t x){size_t i = x / 8;size_t j = x % 8;_bits[i] &= ~(1 << j);}//检测x映射的bit是否为1bool test(size_t x){size_t i = x / 8;size_t j = x % 8;return _bits[i] & (1 << j);}private:vector<char> _bits;
};

5.1.3 位图的应用

5.1.3.1

给定100亿个整数,设计算法找到只出现一次的整数?

解决方法:

  1. 2位位图:00表示没有出现;01表示出现1次;10表示出现2次及以上。1Byte能表示4个数字的出现次数,但是需要修改class bitset的代码。
  2. 2个位图:也是用两个bit表示数字的出现次数,但是是把两个位图拼起来,可以直接复用class bitset的代码。
template<size_t N>
class bitset
{
public://构造函数bitset(){_bits.resize(N / 8 + 1, 0);//N/8只保留整数部分,为防止空间不够,再+1}//将x映射的bit置1void set(size_t x){size_t i = x / 8;size_t j = x % 8;_bits[i] |= (1 << j);}//将x映射的bit置0void reset(size_t x){size_t i = x / 8;size_t j = x % 8;_bits[i] &= ~(1 << j);}//检测x映射的bit是否为1bool test(size_t x){size_t i = x / 8;size_t j = x % 8;return _bits[i] & (1 << j);}private:vector<char> _bits;
};template<size_t N>
class twobitset
{
public://修改x的出现次数的状态void set(size_t x){//00 -> 01if (_bs1.test(x) == false && _bs2.test(x) == false){_bs2.set(x);}//01 -> 10else if (_bs1.test(x) == false && _bs2.test(x) == true){_bs1.set(x);_bs2.reset(x);}//10不变}//打印只出现一次(01)的整数void Print(){for (size_t i = 0; i < N; ++i){if (_bs2.test(i))//因为没有11的状态,所以只要第二个bit是1,那就是01的状态{cout << i << endl;}}}public:bitset<N> _bs1;bitset<N> _bs2;
};

5.1.3.2

给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

解决方法:

  1. 将一个文件的数字读取到内存的一个位图中,再读取另一个文件,判断其中的数字在不在位图中。这样得到的交集可能会有重复的数字,需要去重;或者,每次找到交集值,都将上面位图对应的值设置为0可以解决找到交集有重复值的问题
  2. 读取文件1的数据映射到位图1,读取文件2的数据映射到位图2。2个位图的对应的位之间进行按位与运算,等于1,则该位对应的数字属于交集。

5.1.3.3

1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数。

解决方法类似5.3.1:00表示没有出现;01表示出现1次;10表示出现2次;11表示出现3次及以上。不超过2次——01 10。

5.2 布隆过滤器

5.2.1 布隆过滤器的提出

我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉
那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 用服务器记录了用
户看过的所有历史记录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那
些已经存在的记录。 如何快速查找呢?

  1. 用哈希表存储用户记录,缺点:浪费空间。
  2. 用位图存储用户记录,缺点:位图一般只能处理整型,如果内容编号是字符串,就无法处理了。
  3. 将哈希与位图结合,即布隆过滤器。

5.2.2 布隆过滤器的概念

布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概
率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存
在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也
可以节省大量的内存空间。

假设有3个哈希函数。

数据"string"对应的哈希值分别为2、3、7,把这3个位置的bit置1:

数据"vector"对应的哈希值分别为1、3、5,把这3个位置的bit置1:

查找数据"list"是否存在:数据"list"对应的哈希值分别为1、4、5,而4这个bit位为0,说明没有任何一个值映射到这个bit位上,因此"list"一定不存在。

查找数据"deque"是否存在:数据"deque"对应的哈希值分别为2、3、5,这3个bit位都为1,但是只能说明"deque"可能存在。因为这3个bit位可能都被其他数据置1了,即使"deque"不存在,"deque"对应的哈希值的bit位也可能全为1。

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。比如:删除上图中"vector",如果直接将该元素所对应的bit位置0,"string"也被删除了,因为这两个元素在多个哈希函数计算出的bit位上刚好有重叠。

一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。

缺陷:

  1. 无法确认元素是否真正在布隆过滤器中
  2. 存在计数回绕

来源:详解布隆过滤器的原理,使用场景和注意事项 - 知乎

5.2.3 布隆过滤器的实现

template<size_t N>
class bitset
{
public://构造函数bitset(){_bits.resize(N / 8 + 1, 0);//N/8只保留整数部分,为防止空间不够,再+1}//将x映射的bit置1void set(size_t x){size_t i = x / 8;size_t j = x % 8;_bits[i] |= (1 << j);}//将x映射的bit置0void reset(size_t x){size_t i = x / 8;size_t j = x % 8;_bits[i] &= ~(1 << j);}//检测x映射的bit是否为1bool test(size_t x){size_t i = x / 8;size_t j = x % 8;return _bits[i] & (1 << j);}private:vector<char> _bits;
};struct BKDRHash
{size_t operator()(const string& s){size_t hash = 0;for (auto ch : s){hash += ch;hash *= 31;}return hash;}
};struct APHash
{size_t operator()(const string& s){size_t hash = 0;for (long i = 0; i < s.size(); i++){size_t ch = s[i];if ((i & 1) == 0){hash ^= ((hash << 7) ^ ch ^ (hash >> 3));}else{hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));}}return hash;}
};struct DJBHash
{size_t operator()(const string& s){size_t hash = 5381;for (auto ch : s){hash += (hash << 5) + ch;}return hash;}
};//N:最多会插入key数据的个数
template<size_t N,class K = string,class Hash1 = BKDRHash,class Hash2 = APHash,class Hash3 = DJBHash>
class BloomFilter
{
public:void set(const K& key){size_t len = N * _X;size_t hash1 = Hash1()(key) % len;_bs.set(hash1);size_t hash2 = Hash2()(key) % len;_bs.set(hash2);size_t hash3 = Hash3()(key) % len;_bs.set(hash3);//cout << hash1 << " " << hash2 << " " << hash3 << " " << endl << endl;}bool test(const K& key){size_t len = N * _X;size_t hash1 = Hash1()(key) % len;if (!_bs.test(hash1)){return false;}size_t hash2 = Hash2()(key) % len;if (!_bs.test(hash2)){return false;}size_t hash3 = Hash3()(key) % len;if (!_bs.test(hash3)){return false;}// 在      不准确的,存在误判// 不在    准确的return true;}
private:static const size_t _X = 4;bitset<N * _X> _bs;
};

5.2.4 布隆过滤器的应用

给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法。

假设平均每个query是50Byte, 100亿个query是5000亿Byte,即500GB。

近似算法:布隆过滤器(有误判)

精确算法:哈希切分

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

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

相关文章

BLE基础理论/Android BLE开发示例

参考&#xff1a;https://blog.csdn.net/qq_36075612/article/details/127739150?spm1001.2014.3001.5502 参考&#xff1a; https://blog.csdn.net/qq_36075612/article/details/122772966?spm1001.2014.3001.5502 目录 蓝牙的分类传统蓝牙低功耗蓝牙 蓝牙专业词汇&#xff…

了解Unity编辑器之组件篇Event(七)

Event&#xff1a;用于在对象之间进行通信和交互的机制。它可以帮助你实现触发和响应特定动作或状态的逻辑一、Event System&#xff1a;用于处理 UI 事件的系统组件 First Selected 属性&#xff1a;定义了在场景加载或 UI 激活时&#xff0c;哪个 UI 元素将成为首选的选中元素…

秋招备战笔试Day1

目录 单选 1. 在 Java 中&#xff0c;存放字符串常量的对象属于&#xff08; &#xff09;类对象。 2.已知如下类定义&#xff1a; 如下哪句可以正确地加入子类中&#xff1f; 3. 下列选项中属于面向对象编程主要特征的是&#xff08;&#xff09; 4.关于下列程序段的输出结…

7年经验之谈 —— 浅谈web性能测试

什么是性能测试&#xff1f; web性能应该注意些什么&#xff1f; 性能测试&#xff0c;简而言之就是模仿用户对一个系统进行大批量的操作&#xff0c;得出系统各项性能指标和性能瓶颈&#xff0c;并从中发现存在的问题&#xff0c;通过多方协助调优的过程。而web端的性能测试…

联发科CEO:未获准向华为供货,换机潮已过去,手机需求不会更差

据钜亨网报道&#xff0c;联发科近期召开了业绩说明会。蔡力行&#xff0c;该公司副董事长兼首席执行官&#xff0c;表明当前手机市场需求保持稳定&#xff0c;并且随着过去两年用户更换潮的过去&#xff0c;对手机市场明年有一定期望。 根据蔡力行的指示&#xff0c;联发科正在…

Notepad++工具通过正则表达式批量替换内容

1.每行末尾新增特定字符串 CtrlH弹出小窗口&#xff1b;查找目标输入$&#xff0c;替换为输入特定字符串&#xff1b;选中循环查找&#xff0c;查找模式选正则表达式&#xff1b;最后点击全部替换 2.每行行首新增特定字符串 CtrlH弹出小窗口&#xff1b;查找目标输入^&…

Windows如何安装Django及如何创建项目

目录 1、Windows安装Django--pip命令行 2、创建项目 2.1、终端创建项目 2.2、在Pycharm中创建项目 2.3、二者创建的项目有何不同 2.4、项目目录说明 1、Windows安装Django--pip命令行 安装Django有两种方式&#xff1a; pip命令行【推荐--简单】手动安装【稍微复杂一丢丢…

无涯教程-jQuery - Puff方法函数

吹气效果可以与show/hide/toggle一起使用。通过按比例放大元素并同时隐藏它&#xff0c;可以形成粉扑效果。 Puff - 语法 selector.hide|show|toggle( "puff", {arguments}, speed ); 这是所有参数的描述- model - 效果的模式。可以是"显…

【嵌入式Linux系统开发】——系统移植概述

目录 &#x1f349;&#x1f349;一、什么是嵌入式系统 &#x1f349;&#x1f349;二、嵌入式系统操作 &#x1f349;&#x1f349;三、嵌入式Linux的特点 &#x1f349;&#x1f349;四、嵌入式系统的组成 1、硬件和软件 2、硬件层 3、中间层 4、软件层 5、 功能层与执…

如何用Python统计CSDN质量分

文章目录 CSDN质量分查询selenium爬取博客地址单篇测试批量查询分析 CSDN质量分查询 CSDN对博客有一套分数评价标准&#xff0c;其查询入口在这里&#xff1a;质量分查询&#xff0c;效果大致如下 如果质量分太低&#xff0c;就会在博文的标题下面出现黄底黄字&#xff1a; 这…

Redis简介,设置redis内存大小,设置redis淘汰机制,查看内存占用情况,内存占用分析

为什么使用Redis缓存数据库 我们日常的开发&#xff0c;无非是对数据的处理。程序的定义也可以这样狭义的解释&#xff1a;算法数据。可见数据库是多么重要的工具。但是关系型数据库的读写能力在200-1000次/秒不等&#xff0c;服务器好点可能更多&#xff0c;这导致在高并发的…

排序链表——力扣148

文章目录 题目描述法一 自顶向下归并排序法二&#xff09;自底向上归并排序 题目描述 题目的进阶问题要求达到 O(nlogn) 的时间复杂度和 O(1) 的空间复杂度&#xff0c;时间复杂度是 O(nlogn) 的排序算法包括归并排序、堆排序和快速排序&#xff08;快速排序的最差时间复杂度是…

【C#】.Net Framework框架下的Authorize权限类

2023年&#xff0c;第31周&#xff0c;第3篇文章。给自己一个目标&#xff0c;然后坚持总会有收货&#xff0c;不信你试试&#xff01; 在C#的.NET Framework中&#xff0c;你可以使用Authorize类来处理权限认证。Authorize类位于System.Web.Mvc命名空间中&#xff0c;它提供了…

关于Docker的知识点

Docker是一个快速交付应用、运行应用的技术。 Docker基本操作--容器 示例&#xff1a;创建运行一个Nginx容器

Python不是一门伟大的语言

作为一门简洁易用、生态蓬勃且具有高泛用性的编程语言&#xff0c;Python一直以来都被不少人称作“编程语言中的瑞士军刀”。 尤其随着近来AI热潮席卷全球&#xff0c;Python在编程语言圈中的地位也随之水涨船高&#xff0c;甚至一度被视作AI专用语言或大数据专用语言。 然而…

QT第四讲

思维导图 基于QT的网络聊天室 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpServer> //服务器类 #include<QTcpSocket> //客户端类 #include<QMessageBox> //对话框类 #include<QList…

二叉树的层序遍历(两种方法:迭代+递归)

题目&#xff1a; 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]] 解题思路&#xff1a;迭代法…

string类的模拟实现

文章目录 string类的模拟实现string基本框架的实现operator的实现string常用函数的实现 string类的模拟实现 前文对于string的常用函数做了讲解&#xff0c;由于string是一个面试官常考的点&#xff0c;总喜欢让模拟实现string类&#xff0c;下面来模拟实现一下string&#xf…

优化企业集成架构:iPaaS集成平台助力数字化转型

前言 在数字化时代全面来临之际&#xff0c;企业正面临着前所未有的挑战与机遇。技术的迅猛发展与数字化转型正在彻底颠覆各行各业的格局&#xff0c;不断推动着企业迈向新的前程。然而&#xff0c;这一数字化时代亦衍生出一系列复杂而深奥的难题&#xff1a;各异系统之间数据…

Vite创建Vue+TS项目引入文件路径报错

使用vite搭建vue3脚手架的时候&#xff0c;发现main.ts中引入App.vue编辑器会报错&#xff0c;但是不影响代码运行。 报错信息&#xff1a;TS2307: Cannot find module ‘./App.vue’ or its corresponding type declarations. 翻译过来是找不到模块或者相关的声明类型&#…