哈希的概念及其操作

哈希概念

顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O( Log2N),搜索的效率取决于搜索过程中元素的比较次数。
理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

当向该结构中:

  • 插入元素
    根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
  • 搜索元素
    对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功

该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash Table)(或者称散列表)
在这里插入图片描述

哈希冲突

不同元素通过相同哈希函数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。
把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。
在这里插入图片描述
所以我们需要进一步的改进哈希函数来解决冲突

哈希函数

引起哈希冲突的一个原因可能是:哈希函数设计不够合理。 哈希函数设计原则

  • 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间
  • 哈希函数计算出来的地址能均匀分布在整个空间中
  • 哈希函数应该比较简单

常用的哈希函数

  1. 直接定制法
    取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B 优点:简单、均匀 缺点:需要事先知道关键字的分布情况 使用场景:适合查找比较小且连续的情况
  2. 除留余数法
    设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址
  3. 平方取中法
    假设关键字为1234,对它平方就是1522756,抽取中间的3位227作为哈希地址; 再比如关键字为4321,对它平方就是18671041,抽取中间的3位671(或710)作为哈希地址 平方取中法比较适合:不知道关键字的分布,而位数又不是很大的情况
  4. 折叠法
    折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些),然后将这几部分叠加
    求和,并按散列表表长,取后几位作为散列地址。折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况
  5. 随机数法
    选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key) = random(key),其中random为随机数函数。通常应用于关键字长度不等时采用此法
  6. 数学分析法
    假设要存储某家公司员工登记表,如果用手机号作为关键字,那么极有可能前7位都是 相同的,那么我们可以选择后面的四位作为散列地址,如果这样的抽取工作还容易出现 冲突,还可以对抽取出来的数字进行反转(如1234改成4321)、右环位移(如1234改成4123)、左环移位、前两数与后两数叠加(如1234改成12+34=46)等方法。数字分析法通常适合处理关键字位数比较大的情况,如果事先知道关键字的分布且关键字的若干位分布较均匀的情况
不论函数设计的多好----都不可能完全的解决哈希冲突

哈希函数的冲突解决

闭散列

也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那
么可以把key存放到冲突位置中的“下一个” 空位置中去

1. 线性探测

  • 插入
    通过哈希函数获取待插入元素在哈希表中的位置如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素

在这里插入图片描述

  • 删除
    采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。比如删除元素34,如果直接删除掉,104查找起来可能会受影响。因此线性探测采用标记的伪删除法来删除一个元素。
    在这里插入图片描述

/ 哈希表每个空间给个标记
// EMPTY此位置空, EXIST此位置已经有元素, DELETE元素已经删除
enum State{EMPTY, EXIST, DELETE};

线性探测实现

规定:表格中元素唯一
线性探测:表格中的元素一定不会存满-----随着元素不断增多,产生哈希冲突的概率就急剧增加

#include<iostream>
using namespace std;
#include<vector>
enum State{ EMPTY, EXIST, DELETE };
template<class T>
class hashtable
{struct Elem  //存储的元素类型{	Elem():_state(EMPTY){}T _data;State _state;};
public:hashtable(size_t capacity):_table(capacity), _size(0){}bool Insert(const T&data){//检测哈希空间是否充足_CheckCapacity();//1.通过哈希函数计算哈希地址size_t hashAddr = HashFunc(data);//2.检测位置是否可以存储while(_table[hashAddr]._state != EMPTY){//检测该位置元素是否有效&&是否为待插入元素if (EXIST == _table[hashAddr]._state&&data == _table[hashAddr]._data){return false;//元素已经存在,不进行插入}//发生哈希冲突++hashAddr;if (hashAddr == _table.capacity())//如果地址越界了,把地址放回0hashAddr = 0;}//3.插入元素_table[hashAddr]._data = data;_table[hashAddr]._state = EXIST;_size++;return true;}int Find(const T&data){size_t hashAddr = HashFunc(data);while (_table[hashAddr]._state != EMPTY){if (EXIST == _table[hashAddr]._state&&data == _table[hashAddr]._data){return hashAddr;}//发生哈希冲突hashAddr++;if (hashAddr == _table.capacity())hashAddr = 0;}return -1;}bool Erase(const T&data){int ret = Find(data);if (-1 != ret){_table[ret]._state = DELETE;--_size;return true;}return false;}size_t Size()const{return _size;}
private:size_t HashFunc(const T&data){return data%_table.capacity();}
private:vector<Elem>_table; //状态表格size_t _size;
};

哈希算法

什么时候增容?

哈希的负载因子

哈希负载因子= 有效元素个数/哈希表的容量
哈希因子越小:产生冲突的概率越低
负载因子控制在0.7左右,性能就还可以
负载因子达到0.7,就需要进行扩容

扩容

void Swap(hashtable<T>& ht){_table.swap(ht._table);swap(_size, ht._size);swap(_total, ht._total);}
//扩容
void  _CheckCapacity()
{if (_total * 10 / _table.capacity() >= 7){//新的哈希表hashtable<T>newHT(_table.capacity() * 2);//奖旧哈希表中有效元素搬移到新的哈希表中//注意:已经删除的元素不用搬移for (size_t i = 0; i < _table.capacity(); ++i){if (_table[i]._data == EXIST)newHT.Insert(_table[i]._data);}Swap(newHT);}
}

线性探测缺陷

容易产生数据的堆积-----产生冲突的元素全部集中在一起,一但产生冲突,可能会引起一连串的冲突,解决方法:二次探测

二次探测

线性探测的缺陷是产生冲突的数据堆积在一块,这与其找下一个空位置有关系,因为找空位置的方式就是挨着往后逐个去找,因此二次探测为了避免该问题,找下一个空位置的方法为:H(i) = ( H(0)+i2 )% m,或者:H(i) = (H(0) -i2 )% m。其中:i = 1,2,3…, 是通过散列函数Hash(x)对元素的关键码 key 进行计算得到的位置,m是表的大小。(i代表第几次探测)

代码实现

enum State{ EMPTY, EXIST, DELETE };
template<class T, bool IsLine = true>
class hashtable
{struct Elem  //存储的元素类型{	Elem():_state(EMPTY){}T _data;State _state;};
public:hashtable(size_t capacity):_table(capacity), _size(0),_total(0){}bool Insert(const T&data){//检测哈希空间是否充足_CheckCapacity();//1.通过哈希函数计算哈希地址size_t hashAddr = HashFunc(data);size_t i = 0;//二次探测中的第几次探测//2.检测位置是否可以存储while(_table[hashAddr]._state != EMPTY){//检测该位置元素是否有效&&是否为待插入元素if (EXIST == _table[hashAddr]._state&&data == _table[hashAddr]._data){return false;//元素已经存在,不进行插入}//发生哈希冲突if (IsLine){DetectiveLine(hashAddr);}else{++i;DetectiveTwice(hashAddr,i);}}//3.插入元素_table[hashAddr]._data = data;_table[hashAddr]._state = EXIST;_size++;_total++;return true;}int Find(const T&data){size_t hashAddr = HashFunc(data);size_t i = 0;while (_table[hashAddr]._state != EMPTY){if (EXIST == _table[hashAddr]._state&&data == _table[hashAddr]._data){return hashAddr;}//发生哈希冲突if (IsLine){DetectiveLine(hashAddr);}else{++i;DetectiveTwice(hashAddr,i);}}return -1;}bool Erase(const T&data){int ret = Find(data);if (-1 != ret){_table[ret]._state = DELETE;--_size;return true;}return false;}size_t Size()const{return _size;}void Swap(hashtable<T,IsLine>& ht){_table.swap(ht._table);swap(_size, ht._size);swap(_total, ht._total);}
private:size_t HashFunc(const T&data){return data%_table.capacity();}//线性探测void DetectiveLine(size_t&hashAddr){hashAddr += 1;if (hashAddr == _table.capacity())hashAddr = 0;}//二次探测void DetectiveTwice(size_t& hashAddr, size_t i){hashAddr = hashAddr + 2 * i + 1;if (hashAddr >= _table.capacity())hashAddr %= _table.capacity();}//扩容void  _CheckCapacity(){if (_total * 10 / _table.capacity() >= 7){//新的哈希表hashtable<T,IsLine>newHT(_table.capacity() * 2);//奖旧哈希表中有效元素搬移到新的哈希表中//注意:已经删除的元素不用搬移for (size_t i = 0; i < _table.capacity(); ++i){if (_table[i]._data == EXIST)newHT.Insert(_table[i]._data);}Swap(newHT);}}
private:vector<Elem>_table; //状态表格size_t _size;	//哈希表中有效元素的个数size_t _total; //哈希表中元素的个数:存在和删除(为了保证哈希表中数据的唯一性,删除位置不能插入元素)
};

研究表明:当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容

闭散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷。

开散列

开散列概念

开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码
归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。每个链表中保存发生哈希冲突的元素

代码实现

//开散列:一个链表的集合---产生相同哈希地址的元素放到同一个链表里
#include<vector>
//哈希表的结点
template<class T>
struct HashNode
{HashNode(const T&data = T()):_pNext(nullptr), _data(data){}HashNode<T>* _pNext;T _data;
};template<class T>
class hashbucket
{typedef HashNode<T> Node;
public:hashbucket(size_t capacity):_table(capacity), _size(0){}~hashbucket(){Clear();}bool Insert(const T& data){//扩容_CheckCapacity();//通过哈希函数计算哈希的桶号size_t bucketNo = HashFunc(data);Node* pCur = _table[bucketNo];while (pCur){if (data == pCur->_data)return false;pCur = pCur->_pNext;}pCur = new Node(data);pCur->_pNext = _table[bucketNo];_table[bucketNo] = pCur;_size++;return true;}bool Erase(const T&data){//先找在哪个链表size_t bucketNo = HashFunc(data);//在链表中找元素Node* pCur = _table[bucketNo];Node* pPrev = nullptr;while (pCur){if (data == pCur->_data){//可以删除if (pCur == _table[bucketNo])_table[bucketNo] = pCur->_pNext;elsepPrev->_pNext = pCur->_pNext;delete pCur;--_size;return true;}else{pPrev = pCur;pCur = pCur->_pNext;}}return false;}Node* Find(const T& data){size_t bucketNo = HashFunc(data);Node* pCur = _table[bucketNo];while (pCur){if (pCur->_data == data)return pCur;pCur = pCur->_pNext;}return nullptr;}size_t Size()const{return _size;}bool Empty()const{return 0 == _size;}void Clear(){for (size_t bucketNo = 0; bucketNo < _table.capacity(); ++bucketNo){Node* pCur = _table[bucketNo];while (pCur){_table[bucketNo] = pCur->_pNext;delete pCur;pCur = _table[bucketNo];}}}/*void _CheckCapacity(){if (_size == _table.capacity())}*///打印函数void PrintHashBucket(){for (int i = 0; i < _table.capacity(); ++i){Node* pCur = _table[i];cout << "table[" << i << "]:";while (pCur){cout << pCur->_data << "-->";pCur = pCur->_pNext;}cout << "NULL" << endl;}}
private:size_t HashFunc(const T&data){//T:可以是任意类型---可能不是整型return data % _table.capacity();}
private:vector<Node*> _table;//每一个链表的首地址size_t _size;  //当前哈希桶里放了多少个元素
};

开散列的增容

在这里插入图片描述
桶的个数是一定的,随着元素的不断插入,每个桶中元素的个数不断增多,极端情况下,可能会导致一个桶中链表节点非常多,会影响的哈希表的性能,因此在一定条件下需要对哈希表进行增容,那该条件怎么确认呢?开散列最好的情况是:每个哈希桶中刚好挂一个节点,再继续插入元素时,每一次都会发生哈希冲突,因此,在元素个数刚好等于桶的个数时,可以给哈希表增容。
将旧哈希桶里的结点直接向新哈希桶里进行搬移,在新哈希桶里不要创建新的结点

void Swap(hashbucket<T>& ht)
{_table.swap(ht._table);swap(_size, ht._size);
}
void _CheckCapacity()
{if (_size == _table.capacity()){//新的哈希桶hashbucket<T> newHT(_table.capacity() * 2);//将旧哈希桶中的结点往新哈希桶中进行搬移for (size_t i = 0; i < _table.capacity(); ++i){Node* pCur = _table[i];//将i号桶中的所有结点搬移到新哈希桶中while (pCur){//将pCur结点从table的i号移除掉_table[i] = pCur->_pNext;//将pCur插入到新哈希桶中size_t bucketNo = newHT.HashFunc(pCur->_data);//采用头插法pCur->_pNext = newHT._table[bucketNo];newHT._table[bucketNo] = pCur;newHT._size++;_size--;pCur = _table[i];}}//新桶和旧桶进行交换this->Swap(newHT);}
}

在这里插入图片描述

哈希表只能存储int类型的元素,如何实现任意类型

template<class T>
struct HashNode
{HashNode(const T&data = T()):_pNext(nullptr), _data(data){}HashNode<T>* _pNext;T _data;
};template<class T>
struct DefD2INIT
{const T& operator()(const T& data){return data;}
};struct Str2INT
{size_t operator()(const string& s){stringstream ss;ss << s;size_t ret;ss >> ret;return ret;}
};
template<class T,class DTOINT = DefD2INIT<T>>
class hashbucket
{typedef HashNode<T> Node;typedef hashbucket<T, DTOINT> Self;
public:hashbucket(size_t capacity):_table(capacity), _size(0){}~hashbucket(){Clear();}bool Insert(const T& data){//扩容_CheckCapacity();//通过哈希函数计算哈希的桶号size_t bucketNo = HashFunc(data);Node* pCur = _table[bucketNo];while (pCur){if (data == pCur->_data)return false;pCur = pCur->_pNext;}pCur = new Node(data);pCur->_pNext = _table[bucketNo];_table[bucketNo] = pCur;_size++;return true;}bool Erase(const T&data){//先找在哪个链表size_t bucketNo = HashFunc(data);//在链表中找元素Node* pCur = _table[bucketNo];Node* pPrev = nullptr;while (pCur){if (data == pCur->_data){//可以删除if (pCur == _table[bucketNo])_table[bucketNo] = pCur->_pNext;elsepPrev->_pNext = pCur->_pNext;delete pCur;--_size;return true;}else{pPrev = pCur;pCur = pCur->_pNext;}}return false;}Node* Find(const T& data){size_t bucketNo = HashFunc(data);Node* pCur = _table[bucketNo];while (pCur){if (pCur->_data == data)return pCur;pCur = pCur->_pNext;}return nullptr;}size_t Size()const{return _size;}bool Empty()const{return 0 == _size;}void Clear(){for (size_t bucketNo = 0; bucketNo < _table.capacity(); ++bucketNo){Node* pCur = _table[bucketNo];while (pCur){_table[bucketNo] = pCur->_pNext;delete pCur;pCur = _table[bucketNo];}}}void Swap( Self& ht){_table.swap(ht._table);swap(_size, ht._size);}void _CheckCapacity(){if (_size == _table.capacity()){//新的哈希桶Self newHT(_table.capacity() * 2);//将旧哈希桶中的结点往新哈希桶中进行搬移for (size_t i = 0; i < _table.capacity(); ++i){Node* pCur = _table[i];//将i号桶中的所有结点搬移到新哈希桶中while (pCur){//将pCur结点从table的i号移除掉_table[i] = pCur->_pNext;//将pCur插入到新哈希桶中size_t bucketNo = newHT.HashFunc(pCur->_data);//采用头插法pCur->_pNext = newHT._table[bucketNo];newHT._table[bucketNo] = pCur;newHT._size++;_size--;pCur = _table[i];}}//新桶和旧桶进行交换this->Swap(newHT);}}void PrintHashBucket(){for (int i = 0; i < _table.capacity(); ++i){Node* pCur = _table[i];cout << "table[" << i << "]:";while (pCur){cout << pCur->_data << "-->";pCur = pCur->_pNext;}cout << "NULL" << endl;}}
private:size_t HashFunc(const T&data){//T:可以是任意类型---可能不是整型return DTOINT ()(data) % _table.capacity();}
private:vector<Node*> _table;//每一个链表的首地址size_t _size;  //当前哈希桶里放了多少个元素
};

除留余数法,最好模一个素数,如何每次快速取一个类似两倍关系的素数?

//素数表,以递进两倍方式给出
const int PRIMECOUNT = 28;
const size_t primeList[PRIMECOUNT] =
{53ul, 97ul, 193ul, 389ul, 769ul,1543ul, 3079ul, 6151ul, 12289ul, 24593ul,49157ul, 98317ul, 196613ul, 393241ul, 786433ul,1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,1610612741ul, 3221225473ul, 4294967291ul
};size_t GetNextPrime(size_t prime)
{size_t i = 0;for (; i < PRIMECOUNT; ++i){//当前素数大于提供的素数if (primeList[i] > prime)//返回return primeList[i];}//返回最后一个return primeList[PRIMECOUNT - 1];
}

开散列与闭散列比较

应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。事实上: 由于开地址法必须保持大量的空闲空间以确保搜索效率,如二次探查法要求装载因子a <= 0.7,而表项所占空间又比指针大的多,所以使用链地址法反而比开地址法节省存储空间。

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

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

相关文章

软件工程---1.概述

软件的特征 抽象&#xff1a; 不可触摸&#xff0c;逻辑实体&#xff0c;可记录&#xff0c;但看不到复制成本低&#xff1a;不受物质材料的限制&#xff0c;不受物理定律或加工过程的制约&#xff0c;与开发成本相比&#xff0c;复制成本很低无折旧、受硬件制约、未完全摆脱手…

软件工程---2.软件过程

三个模型 瀑布模型增量模型集成和配置模型 没有适用于所有不同类型软件开发的过程模型。 瀑布模型 需求定义系统和软件的设计实现与单元测试集成与系统测试运行与维护 瀑布模型的特征 从上一项活动中接受该项活动的工作成果&#xff08;工作产品&#xff09;&#xff0c;作…

软件工程---4.需求工程

需求工程定义 找出、分析、文档化并且检查需求的过程被称为需求工程 需求的两个描述层次 用户需求&#xff0c;指高层的抽象需求。使用自然语言、图形描述需求。系统需求&#xff0c;指底层的详细需求。使用系统需求文档&#xff08;有时被称为功能规格说明&#xff09;应该…

软件工程---5.系统建模

从不同视角对系统建模 外部视角&#xff0c;上下文模型&#xff0c;对系统上下文或环境建模交互视角&#xff0c;交互模型&#xff08;功能模型&#xff09;&#xff0c;对系统与参与者或系统内构件之间的交互建模结构视角&#xff0c;结构模型&#xff08;静态模型&#xff0…

软件工程---6.体系结构设计

体系结构模型是什么&#xff1f; 体系结构模型&#xff0c;该模型描述系统如何被组织为一组相互通信的构件 体系结构分类 小体系结构关注单个程序的体系结构。在这个层次上&#xff0c;我们关注单个的程序是如何补分解为构件的。大体系结构关注包括其他系统、程序和程序构件…

【剑指offer】_07 矩形覆盖

题目描述 我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形&#xff0c;总共有多少种方法&#xff1f; 解题思路 依旧是斐波那契数列 2n的大矩形&#xff0c;和n个21的小矩形 其中target*2为大矩阵的大小 有以下几种情形…

软件工程---07.设计与实现

软件设计和软件实现 软件设计是一个创造性的活动&#xff0c;在此活动中需要基于客户需求识别软件构件及其关系。软件实现是将设计实现为一个程序的过程 为开发一个系统设计&#xff0c;你需要 理解并定义上下文模型以及系统的外部交互设计系统体系结构识别系统中的主要对象…

软件工程---15.软件复用

复用的图(牢记) 软件复用的好处 开发加速有效的专家利用提高可依赖性降低开发成本降低过程风险符合标准 软件复用的缺点 创建&#xff0c;维护以及使用一个构件库查找&#xff0c;理解以及适配可复用构件维护成本增加缺少工具支持“不是在这里发明的”综合症 应用框架 现在…

软件工程---16.基于构件的软件工程

CBSE CBSE是定义、实现、集成或组装松散耦合的独立构件成为系统的过程。 基于构件的软件工程的要素有: 完全由接口进行规格说明的独立构件。构件标准使构件集成变得更为容易。中间件为构件集成提供软件支持。开发过程适合基于构件的软件工程。 CBSE的设计原则 构件是独立的…

软件工程---17.分布式软件工程

分布式系统的5个优点 资源共享开放性并发性可伸缩性容错性 分布式计算中必须考虑的设计问题 透明性&#xff1a;隐藏底层分布 开放性 可伸缩性 三个维度 规模&#xff1a;又分为增强扩展(单挑)&#xff0c;增加扩展(群殴)分布可靠性 信息安全性 主要防止以下类型的攻击 拦…

软件工程---18.面向服务的软件工程

什么是Web服务 一个松耦合、可复用的软件构件&#xff0c;封装了离散的功能&#xff0c;该功能是分布式的并且可以被程序访问。Web服务是通过标准互联网和基于XML的协议被访问的服务。 服务和软件构件之间的一个重要的区别是 服务应该总是独立的和松耦合的Web 服务没有“请求…

【剑指offer】_09二叉搜索树的后序遍历序列

题目描述 输入一个整数数组&#xff0c;判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。 解题思路 比如下面的这棵二叉搜索树 它的后序遍历为0214369875&#xff1b; 我们设当前根节点为root; 第一次…

【剑指offer】_12 数组中的逆序对

题目描述 在数组中的两个数字&#xff0c;如果前面一个数字大于后面的数字&#xff0c;则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007 解题思路 剑指offer的解法 看到这个题目&#xff0…

详解Linux下通过yum安装Mariadb/MySQL数据库(腾讯云也适用)

1. 安装Mariadb 安装命令 yum -y install mariadb mariadb-server安装完成MariaDB&#xff0c;首先启动MariaDB systemctl start mariadb设置开机启动 systemctl enable mariadbMariaDB的相关简单配置 此命令进入到配置相关界面 mysql_secure_installation首先是设置密码…

海量数据处理(位图和布隆过滤器)

哈希切割 给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址&#xff1f; 与上题条件相同&#xff0c;如何找到top K的IP&#xff1f;如何直接用Linux系统命令实现 解决思路 找到出现次数最多的IP地址 要找到前TopK的IP地址&#xff0c;就…

C++中的lambda表达式和线程库

98中的一个例子 如果想要对一个数据集合中的元素进行排序&#xff0c;可以使用std::sort方法 #include <algorithm> #include <functional> int main() {int array[] {4,1,8,5,3,7,0,9,2,6};// 默认按照小于比较&#xff0c;排出来结果是升序std::sort(array, a…

文件压缩(Huaffman树的概念及其实现)

什么是压缩 想办法让源文件变得更小并能还原。 为什么要进行文件压缩 文件太大&#xff0c;节省空间提高数据再网络上传输的效率对数据有保护作用—加密 文件压缩的分类 无损压缩 源文件被压缩后&#xff0c;通过解压缩能够还原成和源文件完全相同的格式 有损压缩 解压缩之…

详解STL中的空间配置器(SGI版本)

空间配置器 1.什么是空间配置器 为各个容器高效的管理空间(空间的申请与回收)的 2.为什么需要空间配置器 各种容器----->可以存放元素---->底层需要空间 new 申请空间 operator new ---->malloc调用构造函数------完成对象的构造 动态内存管理总结 前面的容器…

大四阶段的社会实践的主要目的是_疫情当前,大三大四的学生“很惨”?大一大二的学生也别松懈...

大四毕业生不容易这次疫情对于高校学生而言&#xff0c;可以说是各有各的难处&#xff0c;“这届毕业生很惨”更是屡上热搜。不可否认&#xff0c;大四毕业生确实很不容易&#xff0c;论文答辩、毕业、求职就业等都受到了影响&#xff0c;虽然有困难&#xff0c;但各方都在积极…

【剑指offer】_19 滑动窗口中的最大值

题目描述 给定一个数组和滑动窗口的大小&#xff0c;找出所有滑动窗口里数值的最大值。例如&#xff0c;如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3&#xff0c;那么一共存在6个滑动窗口&#xff0c;他们的最大值分别为{4,4,6,6,6,5}&#xff1b; 针对数组{2,3,4,2,6,2,…