当前位置: 首页 > news >正文

哈希知识详解

目录

一、哈希

二、哈希函数

  1、直接定值法

  2、除留余数法

三、哈希冲突

四、哈希冲突解决

1、闭散列(开放定值法)

         闭散列代码

 2、哈希桶

          哈希桶的结构

         查找方法

        插入方法

        删除方法

         析构

        迭代器 

        完整哈希桶代码 

五、通过哈希桶封装 unordered_map 和 unordered_set


一、哈希

以前,在面对海量数据的查找时,最快就是红黑树 logN,无法满足需求。

于是探索出了另一种用关键字 key 值与其存储位置建立映射的思想,让查找效率提升到 O(1) ,这个就是哈希。

二、哈希函数

  1、直接定值法

        ①、直接定值法:用当前的值与数组建立一一对应的关系。

        例如,a,b,c,d 在数组 v 上的映射为 v[0],v[1],v[2],v[3]

        ②、直接定值法的缺点:如果给的值直接跨度较大就非常消耗空间。

        例如:1,1000 在数组 v 上的映射为 v[1] v[1000] 。为了存两个数开了1000多空间,十分浪费。

  2、除留余数法

        除留余数法就是:映射位置为 key 值模上空间大小。( i = key % 空间大小 )

三、哈希冲突

        用上述方法可能会有哈希冲突的问题(也就是不同的 key 值 映射到了同一个地方)

        如:除留余数法,空间大小为7,key值 1 和 8 会映射在同一个地方(1%7 = 8%7 = 1)

四、哈希冲突解决

1、闭散列(开放定值法)

        假设空间大小为 N

        线性探测:假设用除留余数法,i = key % N,如果找到的当前位置有值了,那就向后找,直到找到空位置,放进去。

        查找:执行 i = (i + 1) % N,不断向后查找(后面找完了,就从第一个位置开始继续找),直到找到空位置。

        可以看到,哈希冲突越多,我们查找的效率越低。因此,我们可以想办法让数据的个数和空间大小维持在一个比例,降低哈希冲突。也就是维持 负载因子 / 载荷因子 的大小

        (负载因子 / 载荷因子 = 数据个数 / 空间大小 )。

        闭散列(开放定值法) 一般将这个比例维持在 0.7 左右。

        删除:如上图,如果我们删除 8,那么查找 16 的时候就找到空位置 2,无法找到 16。因此,我们删除不能只是简单的将值删除,还应该将表的每个位置增设标志位。

 

         闭散列代码

        插入:

        1、判断当前 负载因子 / 载荷因子 是否超过 0.7,如果超过,就扩容。

        2、因为扩容后的空间大小变化了,因此,所有的数据需要重新插入,我们可以定义一个临时对象 HashTable temp,把当前所有数据插入 temp 中,再将 temp 中的数据换出来,完成重新插入。

        3、根据新的空间大小和 key 值,找到映射位置。如果当前位置有值,就向后查找空闲位置(标志位不为 EXIST),找到就插入

        查找:

        1、先判断当前 key 值的映射位置 keyi 是否正确,如果不对,继续向后查找

        2、 如果查找到了,返回下标;如果没找到,找了一圈回到 keyi ,则返回 -1

        删除:

        1、通过 key 用 Find 找到 keyi,如果不存在,就返回

        2、如果存在,则将标志位设为 DELETE 并 --_size  

// 定义的状态
enum Status
{EMPTY,EXIST,DELETE
};// 哈希表中的数据
template<class K, class V>
struct HashData
{typedef HashData<K, V> self;
public:HashData():_status(EMPTY){}/*HashData(const std::pair<K, V>& value){_data = value;_status = EXIST;}self& operator=(const self& val){_data = val._data;_status = val._status;return *this;}*/
public:std::pair<K, V> _data; // 数据Status _status; // 状态
};template<class K, class V>
class HashTable
{
public:static const double rate;
public:HashTable(int n = 0){_table.resize(n);}void Insert(const std::pair<K, V>& value){// 空间大小int n = _table.size();// 如果比例超过 rate 就扩容if (_size >= rate * n){// 扩容为原来的 2 倍 (哈希表的大小最好为素数,这里就简化一下)int newcapacity = n == 0 ? 4 : 2 * n;// 因为扩容后空间大小变了,因此每个数据的映射位置也会变化,所以要重新插入// 定义一个临时的哈希表HashTable<K, V> temp(newcapacity);// 把原来的数据重新插入新表中for (auto e : _table){if(e._status == EXIST)temp.Insert(e._data);}// 将新表换出来_table.swap(temp._table);_size = temp._size;n = newcapacity;}// 通过除留余数法 找到对应下标int keyi = value.first % n;// 找到第一个标记位不为 EXIST 的位置,放入while (_table[keyi]._status == EXIST){keyi = (keyi + 1) % n;}_table[keyi]._data = value;_table[keyi]._status = EXIST;_size++;}// 找到了就返回下标,没找到返回 -1int Find(const K& key){int n = _table.size();// 通过除留余数法 找到对应下标int keyi = key % n;if (_table[keyi]._data.first == key){return keyi;}// 从第 2 个位置继续查找int pos = (keyi + 1) % n;while (_table[pos]._status != EMPTY){// 转一圈回来了,全是 DELETE 和 EXISTif (pos == keyi)break;if (_table[pos]._data.first == key){return pos;}pos = (pos + 1) % n;}return -1;}void Erase(const K& key){int n = _table.size();// 如果没找到,就直接返回int pos = Find(key);if (pos == -1) return;// size-- 并设置标志位_table[pos]._status = DELETE;_size--;}private:std::vector<HashData<K, V>> _table; // 哈希表int _size; // 当前负载
};
template<class K, class V>
const double HashTable<K, V>::rate = 0.7; // 比例为 0.7

 2、哈希桶

        与闭散列不同,哈希桶(也叫做拉链法) 是在对应位置放单链表,当有哈希冲突的时候,就把值尾插挂在对应位置的单链表下。

        优化:当一个位置的单链表的节点个数超过 8 个时,可以考虑挂红黑树。


          哈希桶的结构

// 哈希表下挂的 单链表的节点
template<class T>
struct HashNode
{HashNode(){}HashNode(const T& val):_val(val){}HashNode<T>* _next = nullptr; // next 指针,指向下一个位置T _val = T(); // 值
};// K 表示 key(关键字) 的类型
// T 表示 值 的类型
// KOFT 表示通过 T 找到对应的 key 的仿函数(封装map和set就可以一起使用了)
// map 的值是 pair<K, T>,set 的值和 key 一样,因此,通过传入仿函数就可以让它们一起使用
// HASH 表示哈希函数,如,string 就需要通过哈希函数转化才能求位置
template<class K, class T, class KOFT, class HASH>
class HashBucket
{// typedefusing node = HashNode<T>;// 设置 负载因子 / 载荷因子 的比例static const double rate;// 静态成员变量,类内定义,类外初始化public:HashBucket(int n = 0){// 将数组的大小开为 n_table.resize(n, nullptr);}private:std::vector<node*> _table;// 哈希表,每个位置存一个单链表size_t _size;// 数据个数
};
template<class K, class T, class KOFT, class HASH>
const double HashBucket<K, T, KOFT, HASH>::rate = 0.7;// 初始化比例

         查找方法

        1、先判断哈希表是否为空

        2、通过哈希函数得到值,用除留余数法得到对应的下标

        3、通过下标找到单链表 _table[keyi] ,在这个单链表中遍历查找即可

iterator Find(const K& key)
{// 如果表是空的,那么不需要查找if (_table.size() == 0)return end();// 通过哈希函数,除留余数法 得到对应下标 keyiHASH hashfunc;int keyi = hashfunc(key) % _table.size();KOFT kot;// 从单链表中查找值node* head = _table[keyi];while (head){// 找到返回if (kot(head->_val) == key){return iterator(head, this);}head = head->_next;}// 没找到返回 end()return end();
}

        插入方法

        1、先判断 key 值是否已经存在,存在就不插入了

        2、如果 负载因子 / 载荷因子 的比例超过 定义的 rate,扩容

                扩容思想和上面的闭散列类似,因为哈希桶的大小发生变化,要重新插入原有值。我们可以先定义一个临时的大小为 newcapacity 的哈希桶,再将原本的数据插入新的临时哈希桶中,只要把临时桶的资源换出来,就完成了重新插入。

        3、通过哈希函数和 key 找到对应下标,通过下标找到该插入的单链表

        4、创建新节点,头插到单链表中,将 _size++ 完成插入 

// 设计前面的 iterator 可以帮助 运算符[], 拿到迭代器
std::pair<iterator, bool> Insert(const T& val)
{// 拿到 key 值KOFT kot;const K& key = kot(val);// 通过Find判断是否存在对应的 keyiterator it = Find(key);if (it != end())return std::make_pair(it, false);int n = _table.size();if (_size >= n * rate){// 如果 size 为空,设置初始容量为 4,否则新容量为原来的 2 倍int newcapacity = _size == 0 ? 4 : 2 * n;// 设置临时对象HashBucket temp(newcapacity);for (node* head : _table){// 因为容量发生变化,所以所有数据的映射位置发生变化// 将所有数据重新插入 tempwhile (head){temp.Insert(head->_val);head = head->_next;}}// 将 temp 中的资源换出来_table.swap(temp._table);_size = temp._size;n = newcapacity;}// 得到对应下标 keyiHASH hashfunc;int keyi = hashfunc(key) % n;// 创建新节点node* newnode = new node(val);// 头插newnode->_next = _table[keyi];_table[keyi] = newnode;// ++size++_size;return std::make_pair(iterator(newnode, this), true);
}

        删除方法

        1、和插入一样,先进行查找,如果没找到 key,就直接返回

        2、通过 key 和哈希函数得到对应的下标,找到对应单链表

        3、问题转化为在单链表中查找节点,找到返回即可

bool Erase(const K& key)
{// 先进行查找,没找到直接返回iterator iter = Find(key);if (iter == end())return false;// 通过 key 得到下标HASH hashfunc;int keyi = hashfunc(key) % _table.size();KOFT kot;node* prev = nullptr;node* cur = _table[keyi];while (cur){if (kot(cur->_val) == key){if (prev == nullptr)_table[keyi] = cur->_next;elseprev->_next = cur->_next;delete cur;break;}prev = cur;cur = cur->_next;}return true;
}

         析构

        遍历哈希表,将所有单链表释放。 

~HashBucket()
{// 释放所有单链表的节点for (node* head : _table){while (head){node* next = head->_next;delete head;head = next;}}
}

        迭代器 

        1、迭代器基本结构 

        哈希桶的迭代器需要节点,因为进行++操作时,需要查找表的下一个非空节点,因此还需要这个哈希桶的指针。

         哈希桶的 operator== 和  operator!= 判断节点的地址是否相同即可

        解引用*和->,* 返回值引用,-> 返回值的地址

        注意:因为通过哈希表指针使用了私有成员 _table ,因此要把迭代器设为友元,或设置 GetTable() 函数返回。

// 声明
template<class K, class T, class KOFT, class HASH>
class HashBucket;template<class K, class T, class KOFT, class HASH>
class __HashIterator
{
public:typedef HashNode<T> node;typedef HashBucket<K, T, KOFT, HASH> HT;typedef __HashIterator<K, T, KOFT, HASH> self;__HashIterator(node* node, HT* ht):_node(node),_ht(ht){}bool operator!=(const self& iter){return _node != iter._node;}bool operator==(const self& iter){return _node == iter._node;}T& operator*(){return _node->_val;}T* operator->(){return &(_node->_val);}node* _node;HT* _ht;
};

        2、迭代器 ++

        ++iterator,前置++,返回自己

        思路:找到下一个不为空的节点,因为是++,所以不需要回到开头,后面都为空就返回。

self& operator++()
{// 如果 _node 为空,说明在 end() 位置,直接返回if (_node == nullptr)return *this;// 如果该节点的 next 不为空,返回它的下一个节点if (_node->_next){_node = _node->_next;return *this;}// 如果它是该链表的最后一个节点,找下一条链表的头//  获取当前节点的映射位置 keyiKOFT kot;HASH hashfunc;const K& key = kot(_node->_val);int keyi = hashfunc(key) % _ht->_table.size();// 向后查找哈希表中不为空的链表while (++keyi < _ht->_table.size()){// 找到,_node 为当前链表的头,也就是 _table[keyi]if (_ht->_table[keyi]){_node = _ht->_table[keyi];break;}}// 没找到,_node 返回空if (keyi == _ht->_table.size())_node = nullptr;return *this;
}

        完整哈希桶代码 

namespace bucket
{// 哈希表下挂的 单链表的节点template<class T>struct HashNode{HashNode(){}HashNode(const T& val):_val(val){}HashNode<T>* _next = nullptr;T _val = T();};// 声明template<class K, class T, class KOFT, class HASH>class HashBucket;template<class K, class T, class KOFT, class HASH>class __HashIterator{public:typedef HashNode<T> node;typedef HashBucket<K, T, KOFT, HASH> HT;typedef __HashIterator<K, T, KOFT, HASH> self;__HashIterator(node* node, HT* ht):_node(node),_ht(ht){}bool operator!=(const self& iter){return _node != iter._node;}bool operator==(const self& iter){return _node == iter._node;}T& operator*(){return _node->_val;}T* operator->(){return &(_node->_val);}self& operator++(){// 如果 _node 为空,说明在 end() 位置,直接返回if (_node == nullptr)return *this;// 如果该节点的 next 不为空,返回它的下一个节点if (_node->_next){_node = _node->_next;return *this;}// 如果它是该链表的最后一个节点,找下一条链表的头//  获取当前节点的映射位置 keyiKOFT kot;HASH hashfunc;const K& key = kot(_node->_val);int keyi = hashfunc(key) % _ht->_table.size();// 向后查找哈希表中不为空的链表while (++keyi < _ht->_table.size()){// 找到,_node 为当前链表的头,也就是 _table[keyi]if (_ht->_table[keyi]){_node = _ht->_table[keyi];break;}}// 没找到,_node 返回空if (keyi == _ht->_table.size())_node = nullptr;return *this;}node* _node;HT* _ht;};template<class K, class T, class KOFT, class HASH>class HashBucket{using node = HashNode<T>;static const double rate;public:using iterator = __HashIterator<K, T, KOFT, HASH>;friend iterator;HashBucket(int n = 0){_table.resize(n, nullptr);}// 设计前面的 iterator 可以帮助 运算符[], 拿到迭代器std::pair<iterator, bool> Insert(const T& val){// 拿到 key 值KOFT kot;const K& key = kot(val);// 通过Find判断是否存在对应的 keyiterator it = Find(key);if (it != end())return std::make_pair(it, false);int n = _table.size();if (_size >= n * rate){// 如果 size 为空,设置初始容量为 4,否则新容量为原来的 2 倍int newcapacity = _size == 0 ? 4 : 2 * n;// 设置临时对象HashBucket temp(newcapacity);for (node* head : _table){// 因为容量发生变化,所以所有数据的映射位置发生变化// 将所有数据重新插入 tempwhile (head){temp.Insert(head->_val);head = head->_next;}}// 将 temp 中的资源换出来_table.swap(temp._table);_size = temp._size;n = newcapacity;}// 得到对应下标 keyiHASH hashfunc;int keyi = hashfunc(key) % n;// 创建新节点node* newnode = new node(val);// 头插newnode->_next = _table[keyi];_table[keyi] = newnode;// ++size++_size;return std::make_pair(iterator(newnode, this), true);}iterator begin(){// 找到第一个非空单链表的头for (auto head : _table){if (head)return iterator(head, this);}return iterator(nullptr, this);}iterator end(){// 为空代表走到了结尾return iterator(nullptr, this);}iterator Find(const K& key){if (_table.size() == 0)return end();// 得到对应下标 keyiHASH hashfunc;int keyi = hashfunc(key) % _table.size();KOFT kot;// 从单链表中查找值node* head = _table[keyi];while (head){// 找到返回if (kot(head->_val) == key){return iterator(head, this);}head = head->_next;}// 没找到返回 end()return end();}bool Erase(const K& key){// 先进行查找,没找到直接返回iterator iter = Find(key);if (iter == end())return false;// 通过 key 得到下标HASH hashfunc;int keyi = hashfunc(key) % _table.size();KOFT kot;node* prev = nullptr;node* cur = _table[keyi];while (cur){if (kot(cur->_val) == key){if (prev == nullptr)_table[keyi] = cur->_next;elseprev->_next = cur->_next;delete cur;break;}prev = cur;cur = cur->_next;}return true;}~HashBucket(){// 释放所有单链表的节点for (node* head : _table){while (head){node* next = head->_next;delete head;head = next;}}}private:std::vector<node*> _table;size_t _size;};template<class K, class T, class KOFT, class HASH>const double HashBucket<K, T, KOFT, HASH>::rate = 0.7;
}

五、通过哈希桶封装 unordered_map 和 unordered_set

        1、封装 unordered_set

        

// 设置哈希函数
template<class K>
class HashFunc
{
public:int operator()(const K& val){return val;}
};
// 特化,string
template<>
int HashFunc<std::string>::operator()(const std::string& str)
{int key = 0;for (char ch : str){key += ch;}return key;
}// 通过 值 找 key
// 对于 unordered_set 关键字和值都是 key
template<class K>
class KOfT
{
public:const K& operator()(const K& key){return key;}
};// 哈希函数给了默认的,也可以外面传
// unordered_set<int> 第一个模版参数是 key
template<class K, class HASH= HashFunc<K>>
class unordered_set
{
public:// typedefusing iterator = typename bucket::HashBucket<K, K, KOfT<K>, HASH>::iterator;// 插入、修改、查找等函数直接调用 哈希桶的接口即可std::pair<iterator, bool> Insert(const K& val){return _ht.Insert(val);}bool Erase(const K& key){return _ht.Erase(key);}iterator Find(const K& key){return _ht.Find(key);}iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}private:// 关键字为 K, 值为 kbucket::HashBucket<K, K, KOfT<K>, HASH> _ht;
};

        2、封装 unordered_map

        哈希桶的 T 为值,在 unordered_set 中为 K,在 unordered_map 中为 pair<K, T> 

        unordered_map 中的 T 为第二个类型,如 unordered_map<string, int> 中,代表的是 int

// 设置的默认哈希函数
template<class K>
class HashFunc
{
public:int operator()(const K& val){return val;}
};// 特化
template<>
int HashFunc<std::string>::operator()(const std::string& str)
{int key = 0;for (char ch : str){key += ch;}return key;
}// 对于 unordered_map 关键字为 K, 值为 pair<K, T>
// 通过 KOfT 就可以实现 unordered_map 和 unordered_set 使用同一个类封装
template<class K, class T>
class KOfT
{
public:const K& operator()(const T& val){return val.first;}
};// unordered_map<string, int>
// K 为key,类型为string 
// T 类型为int
template<class K, class T, class HASH = HashFunc<K>>
class unordered_map
{
public:using iterator = typename bucket::HashBucket<K, std::pair<K, T>, KOfT<K, std::pair<K, T>>, HASH>::iterator;std::pair<iterator, bool> Insert(const std::pair<K, T>& val){return _ht.Insert(val);}bool Erase(const K& key){return _ht.Erase(key);}iterator Find(const K& key){return _ht.Find(key);}T& operator[](const K& key){// 运算符[] 就是如果存在,返回值引用;如果不存在,插入{key, 0},并返回值引用// 因此,我们只要插入{key, 0} 即可std::pair<iterator, bool> ret = Insert(std::make_pair(key, 0));return ret.first->second;}iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}
private:// 调用哈希桶时,key 为 K,值为pair<K, T>bucket::HashBucket<K, std::pair<K, T>, KOfT<K, std::pair<K, T>>, HASH> _ht;
};

        感谢观看♪(・ω・)ノ

相关文章:

哈希知识详解

目录 一、哈希 二、哈希函数 1、直接定值法 2、除留余数法 三、哈希冲突 四、哈希冲突解决 1、闭散列(开放定值法) 闭散列代码 2、哈希桶 哈希桶的结构 查找方法 插入方法 删除方法 析构 迭代器 完整哈希桶代码 五、通过哈希桶封装 unordered_map 和 unordered_set 一、…...

分库分表基本概念讲解

一、基本概念 产生背景 在数据爆炸的年代&#xff0c;单表数据达到千万级别&#xff0c;甚至过亿的量&#xff0c;都是很常见的情景。这时候再对数据库进行操作就是非常吃力的事情了&#xff0c;select个半天都出不来数据&#xff0c;这时候业务已经难以维系。不得已&#xf…...

【DRAM存储器四十二】LPDDR5介绍--LPDDR5的bank架构还能配,为什么8B模式只支持BL32?

👉个人主页:highman110 👉作者简介:一名硬件工程师,持续学习,不断记录,保持思考,输出干货内容 参考资料:《某LPDDR5数据手册》 、《JESD209-5C》 目录 BG mode 16B mode 8B mode...

ubuntu 新建脚本shell并增加图标 双击应用实现python运行

1.使用nano创建shell脚本文件 需要在终端窗口中输入“nano”以打开文本编辑器。 nano 在创建脚本文件前&#xff0c;我们要了解脚本文件是如何运行的&#xff1a; 直接运行&#xff1a;直接在终端直接输入需要运行的脚本文件名称&#xff0c;系统或用缺省版本的shell运行脚…...

【0x0006】HCI_Disconnect命令详解

目录 一、命令概述 二、命令格式和参数说明 2.1. HCI_Disconnect 命令格式 2.2. Connection_Handle 2.3. Reason 三、返回事件说明 3.1. HCI_Command_Status 事件 3.2. HCI_Disconnection_Complete事件 3.3. HCI_LE_CIS_Established事件(针对CIS) 四、事件执行流程…...

【C语言练习(5)—回文数判断】

C语言练习&#xff08;5&#xff09; 文章目录 C语言练习&#xff08;5&#xff09;前言问题问题解析结果总结 前言 通过回文数练习&#xff0c;巩固数字取余和取商如何写代码 问题 输入一个五位数判断是否为回文数&#xff1f; 问题解析 回文数是指正读反读都一样的整数。…...

【全连接神经网络】核心步骤及其缺陷

前向传播 计算公式&#xff08;其中一种&#xff09; x1/x2&#xff1a;输入值&#xff0c;一般是神经网络上一层的输出或者输入数据本身&#xff0c;上图中表示两个节点w11 w13&#xff1a;权重&#xff0c;在神经网络中&#xff0c;权重是学习的参数&#xff0c;表示每个输入…...

【Vulkan入门】11-CreateCommandBuffer

[TOC]目录 先叨叨 先叠个甲&#xff0c;我个人都觉得本篇有点灌水的嫌疑。 到上篇为止&#xff0c;已经创建好了Pipeline。接下来就是要给Pipeline发指令进行渲染了。 从一开始就不断强调&#xff0c;发给Vulkan的指令要通过Queue发送给Device。命令不是一条一条推到Queue中的…...

Redis原理—4.核心原理摘要

大纲(9870字) 1.Redis服务器的Socket网络连接建立 2.Redis多路复用监听与文件事件模型 3.基于队列串行化的文件事件处理机制 4.完整的Redis Server网络通信流程 5.Redis串行化单线程模型为什么能高并发 6.Redis内核级请求处理流程与原理 7.Redis通信协议与内核级请求数据…...

面向对象系统的分析和设计

来源&#xff1a;《设计模式精解-GOF23种设计模式解析》 作者&#xff1a;k_eckel k_eckels mindview - 博客园 (cnblogs.com) --------- 面向对象系统的分析和设计实际上追求的就是两点&#xff1a; &#xff08;1&#xff09;高内聚 &#xff08;2&#xff09;低耦合 …...

单片机:实现交通信号灯(附带源码)

使用单片机实现交通信号灯控制系统是一个经典的嵌入式系统应用。这个项目可以帮助你理解如何通过单片机控制不同颜色的LED灯、处理时间控制、以及输入输出的基本操作。通过这个项目&#xff0c;你将掌握如何设计交通信号灯的时序控制、如何实现定时控制交通灯的切换、以及如何与…...

小白如何学习看懂CAD图纸?

首先&#xff0c;你需要了解CAD图纸的基本构成&#xff0c;包括图例、尺寸标注、比例等等。接着&#xff0c;你可以通过一些专业的书籍、在线课程或视频教程来逐步学习如何识别和理解这些元素。但建议不要学的太复杂了。 掌握基本概念&#xff1a; 坐标系&#xff1a;了解CAD…...

HarmonyOS-高级(一)

文章目录 一次开发、多端部署自由流转 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;HarmonyOS专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年12月09日12点19分 一次开发、多端部署 布局能力 自适应布局 拉伸能力均分能力占比能力缩放…...

Datawhale AI 冬令营(第一期)定制你的第一个专属模型-学习笔记

最近我报名参加了Datawhale组织的主题为“动手学系列&#xff0c;人人都能应用的AI”的Datawhale AI冬令营&#xff08;第一期&#xff09;。 本次学习一共12天&#xff0c;从12月10日-12月21日&#xff0c;学习会包含【跑通速通手册】&#xff0c;【学习大模型微调&数据集…...

群控系统服务端开发模式-应用开发-登录退出发送邮件

一、登录成功发送邮件 在根目录下app文件夹下controller文件夹下common文件夹下&#xff0c;修改Login.php&#xff0c;代码如下 <?php /*** 登录退出操作* User: 龙哥三年风水* Date: 2024/10/29* Time: 15:53*/ namespace app\controller\common; use app\controller\Em…...

app-2 App 应用抓包之 Postern+Charles

一、前言 本篇是基于 Postern Charles 方式对安卓应用数据包进行抓取。可以抓取到市面上大多数的app数据包。 二、环境准备 postern&#xff1a;postern下载地址 charles&#xff1a;Charles 4.5.6 中文版(便携免安装).rar 提取码&#xff1a;6d8f 三、配置及抓包测试 3.…...

cnocr配置及训练测试

cnocr配置及训练测试 1&#xff0c;相关链接2&#xff0c;已有模型调用测试&#xff08;1&#xff09;下载相关模型&#xff08;2&#xff09;Cnstd文本检测模型&#xff08;3&#xff09;模型调用解析脚本 3&#xff0c;自定义数据集训练测试&#xff08;1&#xff09;标签转换…...

【优选算法 前缀和】前缀和算法模板详解:一维前缀 & 与二维前缀和

一维前缀和 题目解析 算法原理 解法一&#xff1a;暴力解法 简单模拟&#xff0c;读完题意有 q 次询问&#xff0c;给哪两个数&#xff0c;就求哪段区间的和并且返回&#xff0c;这样的做法&#xff0c;时间复杂度为O(N*q)&#xff0c;这个时间复杂度会超时&#xf…...

【记录】用JUnit 4的@Test注解时报错java.lang.NullPointerException的原因与解决方法

项目场景&#xff1a; 在练习黑马点评的逻辑过期解决缓存击穿时&#xff0c;编写了一个预热缓存数据的单元测试 SpringBootTest public class HmDianPingApplicationTests {Resourceprivate ShopServiceImpl shopService;Testpublic void testSaveShop() throws InterruptedE…...

Transformer入门(6)Transformer编码器的前馈网络、加法和归一化模块

文章目录 7.前馈网络8.加法和归一化组件9.组合所有编码器组件构成完整编码器 7.前馈网络 编码器块中的前馈网络子层如下图所示&#xff1a; 图1.32 – 编码器块 前馈网络由两个带有ReLU激活函数的全连接层组成。全连接层&#xff08;Fully Connected Layer&#xff09;有时也…...

(七)腾讯cloudstudio+Stable-Diffusion-webui AI绘画教程-安装Stable-Diffusion-WebUI

一、说明 本文选择安装stable-diffusion-webui最新版本 cloud studio 免费版最大的问题是空间不足&#xff0c;我晚上上传时超过了硬盘大小&#xff0c;直接不能启动&#xff0c;没办法&#xff0c;删除&#xff0c;又建了一个工作空间 二、安装 1、打开终端 2、配置Git代理…...

算法基础Day7(动态规划)

文章目录 1.题目2.题目解答1.第N个泰波那契数题目及题目解析动态规划算法学习1.状态表示2.状态转移方程3.初始化4.填表顺序5.空间优化 代码提交空间优化 2.三步问题题目及题目解析算法学习代码提交 1.题目 1137. 第 N 个泰波那契数 - 力扣&#xff08;LeetCode&#xff09;面试…...

代理IP地址和端口是什么?怎么进行设置?

保护个人隐私、突破地域限制、提升网络安全性是我们不断追求的目标。IP地址与端口一种实现这些目标的重要工具。但是&#xff0c;你可能对它是什么&#xff0c;以及如何设置感到困惑。别担心&#xff0c;本文将为你揭开这些神秘的面纱&#xff0c;让你轻松掌握这项技能。 1.IP…...

一文详解TCP协议 [图文并茂, 明了易懂]

欢迎来到啊妮莫的学习小屋! 目录 什么是TCP协议 TCP协议特点✨ TCP报文格式 三次握手和四次挥手✨ 可靠性 效率性 基于字节流✨ 基于TCP的应用层协议 什么是TCP协议 TCP(传输控制协议, Transmission Control Protocol) 是一种面向连接的, 可靠的, 基于字节流的传输层通…...

js后端开发之Next.js、Nuxt.js 与 Express.js

后端js之Next.js、Nuxt.js 与 Express.js 在现代 Web 开发中&#xff0c;JavaScript 已经成为前后端通用的编程语言&#xff0c;而选择合适的后端框架则是构建高效、可扩展应用程序的关键。本文将带你深入了解三个流行的 JavaScript 后端框架&#xff1a;Next.js、Nuxt.js 和 …...

人工智能概要

目录 前言1.什么是人工智能&#xff08;Artificial Intelligence, AI&#xff09;2.人工智能发展的三次浪潮2.1 人工智能发展的第一次浪潮2.2 人工智能发展的第二次浪潮2.3 人工智能发展的第三次浪潮 3.人工智能发展的必备三要素3.1 数据3.2 算法&#xff08;algorithm&#xf…...

spring boot 3集成swagger

Spring Boot 3 集成 Swagger 的过程与之前版本相比有一些变化&#xff0c;主要是因为 springfox 库已经停止更新&#xff0c;并且不再支持新的 Spring Boot 版本。因此&#xff0c;对于 Spring Boot 3 来说&#xff0c;推荐使用 springdoc-openapi 作为集成 Swagger 的解决方案…...

【PlantUML系列】状态图(六)

一、状态图的组成部分 状态&#xff1a;对象在其生命周期内可能处于的条件或情形&#xff0c;使用 state "State Name" as Statename 表示。初始状态&#xff1a;表示对象生命周期的开始&#xff0c;使用 [*] 表示。最终状态&#xff1a;表示对象生命周期的结束&…...

前端缓存页面处理方法

当前一个前端应用新发布时&#xff0c;重新编译后&#xff0c;原来引用的资源文件名都会有变化。如果这个应用的页面在前端浏览器中有缓存&#xff0c;则会导致加载资源失败。怎样去除这种缓存&#xff0c;同时也能尽可能的保证前端访问的性能 ChatGPT said: ChatGPT 这是一个经…...

每日一题 284. 窥视迭代器

284. 窥视迭代器 想要提前知道下一个内容&#xff0c;就需要缓存 class PeekingIterator : public Iterator { public:PeekingIterator(const vector<int>& nums) : Iterator(nums) {// Initialize any member here.// **DO NOT** save a copy of nums and manipula…...

Cesium-(Primitive)-(BoxGeometry)

含实现代码 GISer世界 效果: 以下是 BoxGeometry 类的构造函数属性,以表格形式展示: 属性名类型默认值描述minimumCartesian3盒子的最小 x, y, 和 z 坐标。maximumCartesian3盒子的最大 x, y, 和 z 坐标。vertexFormatVertexFormatVertexFormat.DEFAULT要计算的顶点属性。以下…...

CSS元素宽高特点、类型转化、显式和隐藏(display)

元素的宽高特点 块级元素 可以设置宽高&#xff0c;不可以和其他元素在一行设置宽高时&#xff0c;元素的宽高为设置的值没有设置宽高时&#xff0c;宽度和父级宽高一样&#xff0c;高度由元素内容决定 行级元素 不可以设置宽高&#xff0c;可以和其他元素在一行元素的宽高…...

上市公司投资效率Biddle模型数据(包括最终数据、原始数据及构造说明)2003-2022年

一、计算方式&#xff1a;参考《Journal of accounting and economics》Biddle G C&#xff0c;构建Biddle模型使用企业投资对成长机会的回归模型来估计企业的投资效率&#xff0c;这里成长机会用销售增长率来衡量。回归模型如下图所示: 二、资料范围&#xff1a;包括原始数据…...

矩阵的乘(包括乘方)和除

矩阵的乘分为两种&#xff1a; 一种是高等代数中对矩阵的乘的定义&#xff1a;可以去这里看看包含矩阵的乘。总的来说&#xff0c;若矩阵 A s ∗ n A_{s*n} As∗n​列数和矩阵 B n ∗ t B_{n*t} Bn∗t​的行数相等&#xff0c;则 A A A和 B B B可相乘&#xff0c;得到一个矩阵 …...

Spring Security6.3 自定义AuthorizationManager问题

项目环境&#xff1a; Springboot3.3.5, 对应的SpringFrameWork6.1&#xff0c;Security为6.3 问题&#xff1a;我想自定义AuthorizationManager接口实现类&#xff0c;在里面判断如果角色为amdin则放行请求&#xff1b; 在AdminAuthorizationManager类的check()方法中pass变量…...

第一部分:基础知识 9 . 视图 --[MySQL轻松入门教程]

在MySQL中,视图(View)是一个命名的SQL查询,它被存储在数据库目录中。视图可以包含来自一个或多个表的数据,并且可以像真实表一样被查询。下面是对MySQL视图的详细讲解: 创建视图 使用 CREATE VIEW 语句来创建视图。语法如下: CREATE [OR REPLACE] [ALGORITHM = {UNDEFIN…...

用GPT零负担学单片机之点亮一颗cpu 第3节 训练or特征匹配?用GPT开发嵌入式

用GPT零负担学单片机之点亮一颗cpu 第3节 训练or特征匹配?AI写代码 大家好,我是小杰学长 如果你是大学生 遇到电子技术 学习 成长 入行难题 我曾经通过大学比赛赚钱 从事嵌入式AI 航天军工 用特别的学习和求职方法线下半年带50+学弟学妹入行开发 主页佳喔威信,给你提供一定资…...

2.6、vue2中侦听属性的变化

2.6.1、侦听属性作用侦听属性的变化其实就是监视某个属性的变化。当被监视的属性一旦发生改变时,执行某段代码。2.6.2、watch配置项监视属性变化时需要使用watch配置项 可以监视多个属性,监视哪个属性,请把这个属性的名字拿过来即可。 i.可以监视Vue的原有属性 ii.如果监视的…...

enable_shared_from_this

用途 struct S {shared_ptr<S> dangerous(){return shared_ptr<S>(this); // dont do this!} };int main() {shared_ptr<S> sp1(new S);shared_ptr<S> sp2 sp1->dangerous();return 0; }考虑以上代码&#xff0c;从一个被shared_ptr管理的struc…...

重生之我在异世界学智力题(2)

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 本文目录 引言智力题&#xff1a;逃离孤岛智力题&a…...

深入解析下oracle的number底层存储格式

oracle数据库中&#xff0c;number数据类型用来存储数值数据&#xff0c;它既可以存储负数数值&#xff0c;也可以存储正数数值。相对于其他类型数据&#xff0c;number格式的数据底层存储格式要复杂得多。今天我们就详细探究下oracle的number底层存储格式。 一、环境搭建 1.…...

prometheus

1.安装&#xff0c;tar包&#xff0c;解压即用 tar xf prometheus-2.33.3.linux-amd64.tar.gz -C /app/tools/ 2.创建软链接 ln -s prometheus-2.33.3.linux-amd64/ /app/tools/prometheus 3.进入目录 cd /app/tools/prometheus 4.运行 ./prometheus 5.此时&#xff0…...

C# 23种设计模式(1)单例模式(单件模式)

一、单例模式介绍 单例模式&#xff08;Singleton Pattern&#xff09;是一种创建型设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供一个全局访问点。这个模式在需要一个对象被共享且全局唯一的情况下非常有用&#xff0c;比如配置对象、日志对象、数据库连接…...

Javaweb:HTML、CSS

学习 资源1 学习资源 2 黑马javaweb HTML 1、基础标签、样式 图片标签&#xff1a;<img> src:绝对路径、相对路径(绝对磁盘路径&#xff0c;网络路径&#xff1b;./当前目录&#xff09;width:宽度&#xff08;百分比&#xff09;height:高度&#xff08;百分比&…...

SmartDV将SDIO系列IP授权给RANiX开发车联网(V2X)产品

双方的合作将增强符合ISO 26262标准的车联网&#xff08;V2X&#xff09;系统的通信和连接能力&#xff0c;加速实现更安全、更智能的汽车系统和车辆创新 加利福尼亚州圣何塞市&#xff0c;2024年12月——灵活、高度可配置、可定制化的半导体设计知识产权&#xff08;IP&#…...

【Android】创建型设计模式—单例模式、工厂模式、建造者模式

单例模式 单例模式&#xff08;Singleton Pattern&#xff09;是一种创建型设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供全局访问点。 单例模式类图&#xff1a; #mermaid-svg-kzf6IdXdYeNOHtP0 {font-family:"trebuchet ms",verdana,arial,sa…...

ida9pro压缩包

资源类型的博客大部分都是为了自己某天换新机了用 下载链接2&#xff1a;ida9.zip 下载链接1&#xff1a;https://mega.nz/folder/yiAiVDAa#T0kogEE7ufqy0x0EpCuOLQ 主目录下该文件为证书文件 ida9中选择它&#xff0c;就可以了...

前端入门之VUE--vue组件化编程

前言 VUE是前端用的最多的框架&#xff1b;这篇文章是本人大一上学习前端的笔记&#xff1b;欢迎点赞 收藏 关注&#xff0c;本人将会持续更新。 文章目录 2、Vue组件化编程2.1、组件2.2、基本使用2.2.1、VueComponent 2、Vue组件化编程 2.1、组件 组件&#xff1a;用来实现…...

C++是如何工作的?

首先来看一个最基本的C程序段。 #include <iostream>int main() {std::cout << "HelloWorld" << std::endl;std::cin.get(); } 第一行 #include 的含义是预处理的意思&#xff0c;这条语句的作用是将一个名为iostream的文件拷贝到源代码中这个…...

JavaScript中的this, 究竟指向什么?

在JavaScript代码的不同位置中&#xff0c;this所指向的数据是不一样的。比如大部分同学都知道&#xff0c;在对象的函数属性方法中&#xff0c;this指向对象本身&#xff1b;在构造函数中&#xff0c;this指向要生成的新对象。事实上&#xff0c;this指向的逻辑不止这几种&…...