【数据结构(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置1
	void set(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;

		_bits[i] |= (1 << j);
	}

	//将x映射的bit置0
	void reset(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;

		_bits[i] &= ~(1 << j);
	}

	//检测x映射的bit是否为1
	bool 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置1
	void set(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;

		_bits[i] |= (1 << j);
	}

	//将x映射的bit置0
	void reset(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;

		_bits[i] &= ~(1 << j);
	}

	//检测x映射的bit是否为1
	bool 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 -> 01
		if (_bs1.test(x) == false && _bs2.test(x) == false)
		{
			_bs2.set(x);
		}
		//01 -> 10
		else 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置1
	void set(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;

		_bits[i] |= (1 << j);
	}

	//将x映射的bit置0
	void reset(size_t x)
	{
		size_t i = x / 8;
		size_t j = x % 8;

		_bits[i] &= ~(1 << j);
	}

	//检测x映射的bit是否为1
	bool 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。

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

精确算法:哈希切分

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

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

相关文章

网络知识介绍

一、TCP 传输控制协议&#xff0c;Transmission Control Protocol。 面向广域网的通信协议&#xff0c;跨域多个网络通信时&#xff0c;为两个通信端点之间提供一条具有如下特点的通信方式&#xff1a; 基于流、面向连接、可靠通信方式、网络状况不佳时尽量降低系统由于重传带…

车载开发智能座舱技术——【Surface渲染流程】

SurfaceFlinger智能座舱技术是一种车载开发中的创新技术&#xff0c;它能够实现高效的图形渲染和多媒体处理&#xff0c;为驾驶员和乘客提供更好的车内体验。本文将介绍SurfaceFlinger智能座舱技术的概念和原理&#xff0c;并详细解析Surface的渲染流程和相关代码示例。 一、S…

无头单链表,有完整测试程序

&#x1f35f;无头单链表 &#x1f47b;无头单链表的所有结点都存储有效信息 &#x1f47b;无头单链表相对带头单链表&#xff0c;在有些涉及更改头节点的函数上需要传二级指针 &#x1f35f;头文件list.h #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #includ…

企业内网终端安全无客户端准入控制技术实践

终端无代理/无客户端准入控制技术因其良好的用户体验而倍受创新企业的青睐。无代理/无客户端准入控制技术&#xff0c;顾名思义&#xff0c;是一种在网络中对终端实施访问控制的方法&#xff0c;无需依赖特定的客户端软件。 不同于银行、医院等传统行业的终端准入控制需求&…

识别万物扫一扫,遇到不认识的物品扫就完事

随着科技的不断发展&#xff0c;移动设备已经成为人们日常生活中必不可少的工具。移动设备上的扫一扫功能&#xff0c;可以通过摄像头扫描物品&#xff0c;识别并获取相关信息&#xff0c;为人们的生活带来了很大的便利。本文将探讨识别万物扫一扫的使用及原理。 识别万物的使用…

概念辨析 | SAR运动补偿和自聚焦技术:深入探索雷达图像

注1:本文系“概念辨析”系列之一,致力于简洁清晰地解释、对比复杂而专业的概念。本次辨析的概念是:合成孔径雷达(SAR)的运动补偿和自聚焦技术。 SAR运动补偿和自聚焦技术:深入探索雷达图像 Synthetic Aperture Radar (SAR) 1 背景介绍 合成孔径雷达(Synthetic Aperture R…

iPhone 8透明屏的透明度高吗?

iPhone 8是苹果公司于2017年推出的一款智能手机&#xff0c;它采用了全新的设计和技术&#xff0c;其中一个亮点就是透明屏。 透明屏是指屏幕具有透明度&#xff0c;可以透过屏幕看到背后的物体。 iPhone 8的透明屏采用了最新的OLED技术&#xff0c;这种技术可以实现更高的对比…

RunnerGo五种压测模式你会配置吗

我们在做性能测试时需要根据性能需求配置不同的压测模式如&#xff1a;阶梯模式。使用jmeter时我们需要安装插件来配置测试模式&#xff0c;为了方便用户使用&#xff0c;RunnerGo内嵌了压测模式这一选项&#xff0c;今天给大家介绍一下RunnerGo的几种压测模式和怎么根据性能需…

shell脚本练习

#include <myhead.h> //递归实现输入一个数,输出这个数的每一位 void fun1(int data) {if(data 0) return;fun1(data/10);printf("%d\t",data%10);}//递归实现输入一个数,输出这个数的二进制 void fun2(int data) {if(data 0) return;fun2(data/2);printf(&q…

Redis7学习笔记01

一、redis7实战教程简洁 1、大纲&#xff1a; ①、适合对象&#xff0c;从小白到熟手&#xff0c;一套全包圆 ②、Redis专题-大厂面试题&#xff0c;含100道 ③、Redis专题-真实需求生产真实案例 ④、Redis7新特性 2、小白篇高阶篇&#xff1a; 3、大厂面试题&#xff1a…

软件供应链的基础:SBOM

软件作为一种强大的工具&#xff0c;可以简化复杂的技术概念&#xff0c;但随着软件不可思议的力量而来的是一个相互关联的软件依赖迷宫&#xff0c;这些依赖常常构成软件开发的基础。这些依赖关系并非没有缺陷&#xff0c;正如我们从 Log4Shell 这样的事件中所了解到的那样。当…

git 常用命令有哪些

Git 是我们开发工作中使用频率极高的工具&#xff0c;下面总结下他的基本指令有哪些&#xff0c;顺便温习一下。 前言 一般项目中长存2个分支&#xff1a; 主分支&#xff08;master&#xff09; 和开发分支&#xff08;develp&#xff09; 项目存在三种短期分支 &#xff1a…

【SQL】-【计算两个varchar类型的timestamp的毫秒差】

背景 TRANSTAMP3、TRANSTAMP2在Oracle数据库中的类型为varchar&#xff0c;但实际保存的值是时间戳timestamp类型&#xff0c;现在要计算二者的毫秒差 Oracle或MySQL extract(second from (to_timestamp(TRANSTAMP3,yyyy-mm-dd hh24:mi:ss.ff) - to_timestamp(TRANSTAMP2,yyy…

数据结构—哈夫曼树及其应用

5.6哈夫曼树及其应用 5.6.1哈夫曼树的基本概念 路径&#xff1a;从树中一个结点到另一个结点之间的分支构成这两个结点间的路径。 结点的路径长度&#xff1a;两结点间路径上的分支数。 树的路径长度&#xff1a;从树根到每一个结点的路径长度之和。记作 TL 结点数目相同的…

安全学习DAY14_JS信息打点

信息打点——前端JS框架 文章目录 信息打点——前端JS框架小节概述-思维导图JS安全概述什么是JS渗透测试&#xff1f;前后端差异JS安全问题流行的Js框架如何判定JS开发应用&#xff1f; 测试方法&#xff08;JS文件的获取以及分析方法1、手工搜索分析2、半自动Burp分析插件介绍…

Vue.js表单输入绑定

对于Vue来说&#xff0c;使用v-bind并不能解决表单域对象双向绑定的需求。所谓双向绑定&#xff0c;就是无论是通过input还是通过Vue对象&#xff0c;都能修改绑定的数据对象的值。Vue提供了v-model进行双向绑定。本章将重点讲解表单域对象的双向绑定方法和技巧。 10.1 实现双…

C语言每日一题:10.不使用+-*/实现加法+找到所有数组中消失的数。

题目一&#xff1a; 题目链接&#xff1a; 思路一&#xff1a; 1.两个数二进制之间进行异或如果不产生进位操作那么两个数的和就是就是两个数进行异或的结果。 举例&#xff1a;5&#xff08;0101&#xff09;2&#xff08;0010&#xff09;进行异或等于&#xff1a;7&#xf…

Unity 使用SharpZipLib解压时报错

报错信息&#xff1a; NotSupportedException: Encoding 936 data could not be found. Make sure you have correct international System.Text.Encoding.GetEncoding (System.Int32 codepage) ICSharpCode.SharpZipLib.Zip.ZipConstants.ConvertToString。 出现问题分析&…

【宝藏系列】Linux 常用磁盘管理命令详解

【宝藏系列】Linux 常用磁盘管理命令详解 文章目录 【宝藏系列】Linux 常用磁盘管理命令详解前言1️⃣ df2️⃣du3️⃣fdisk&#x1f4df;磁盘格式化&#x1f4e0;磁盘检验⌨️磁盘挂载与卸除&#x1f4c0;卸载/dev/hdc6 前言 Linux磁盘管理常用三个命令为df、du和fdisk。 df…

无涯教程-Lua - 文件I/O

I/O库用于在Lua中读取和处理文件。 Lua中有两种文件操作&#xff0c;即隐式(Implicit)和显式(Explicit)操作。 对于以下示例&#xff0c;无涯教程将使用例文件test.lua&#xff0c;如下所示。 -- sample test.lua -- sample2 test.lua 一个简单的文件打开操作使用以下语句。…