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

深入理解并实现自定义 unordered_map 和 unordered_set

亲爱的读者朋友们😃,此文开启知识盛宴与思想碰撞🎉。

快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。


          在 C++ 的标准模板库(STL)中,unordered_mapunordered_set是非常实用的容器,它们基于哈希表实现,能够提供高效的查找、插入和删除操作。在 SGI - STL30 版本中,虽然没有标准的unordered_mapunordered_set,但有类似功能的hash_maphash_set,它们作为非标准容器存在。

 本文将基于哈希表来模拟实现自定义的unordered_mapunordered_set。


目录

💯模拟实现的基础 —— 哈希表

1. HashTable 类

1.1 迭代器相关函数

Iterator Begin()

Iterator End()

ConstIterator Begin() const 和 ConstIterator End() const

1.2 插入函数 Insert

1.3 查找函数 Find

1.4 删除函数 Erase

1.5 辅助函数 __stl_next_prime

2. HTIterator 类 

2.1 构造函数 HTIterator(Node* node, const HashTable * pht)

2.2 解引用操作符 operator*()

2.3 箭头操作符 operator->()

2.4 不等于操作符 operator!=

2.5 前置递增操作符 operator++()

💯基于哈希表实现 unordered_set

💯基于哈希表实现 unordered_map

 operator[] 函数

💯测试代码


💯模拟实现的基础 —— 哈希表

首先,定义哈希表的节点结构HashNode。每个节点包含数据_data和指向下一个节点的指针_next,用于解决哈希冲突(链地址法)。

namespace zdf {
// 哈希表节点定义
template<class T>
struct HashNode {T _data;HashNode<T>* _next;// 构造函数,初始化数据和指针HashNode(const T& data) : _data(data), _next(nullptr) {}
};
}
  • 功能:这是哈希表中节点的基本结构,用于存储数据和指向下一个节点的指针,采用链表法解决哈希冲突。
  • 构造函数 HashNode(const T& data)
    • 参数data 是要存储在节点中的数据,类型为 T
    • 作用:初始化节点的数据成员 _data 为传入的 data,并将指向下一个节点的指针 _next 初始化为 nullptr

1. HashTable 类

 

接着,定义哈希表类HashTable。它包含多个成员变量和成员函数,以实现哈希表的各种功能。

namespace zdf {
template<class K, class T, class KeyOfT, class Hash>
class HashTable {// 定义节点类型typedef HashNode<T> Node;
public:// 定义迭代器类型typedef HTIterator<K, T, T*, T&, KeyOfT, Hash> Iterator;// 定义常量迭代器类型typedef HTIterator<K, T, const T*, const T&, KeyOfT, Hash> ConstIterator;// 返回哈希表起始位置的迭代器Iterator Begin() {if (_n == 0) {return End();}for (size_t i = 0; i < _tables.size(); ++i) {Node* cur = _tables[i];if (cur) {return Iterator(cur, this);}}return End();}// 返回哈希表结束位置的迭代器Iterator End() {return Iterator(nullptr, this);}// 返回常量哈希表起始位置的迭代器ConstIterator Begin() const {if (_n == 0) {return End();}for (size_t i = 0; i < _tables.size(); ++i) {Node* cur = _tables[i];if (cur) {return ConstIterator(cur, this);}}return End();}// 返回常量哈希表结束位置的迭代器ConstIterator End() const {return ConstIterator(nullptr, this);}// 插入数据到哈希表pair<Iterator, bool> Insert(const T& data) {KeyOfT kot;Iterator it = Find(kot(data));if (it != End()) {return make_pair(it, false);}Hash hs;size_t hashi = hs(kot(data)) % _tables.size();// 负载因子达到1时进行扩容if (_n == _tables.size()) {vector<Node*> newtables(__stl_next_prime(_tables.size() + 1), nullptr);for (size_t i = 0; i < _tables.size(); ++i) {Node* cur = _tables[i];while (cur) {Node* next = cur->_next;// 计算在新表中的位置并插入size_t newHashi = hs(kot(cur->_data)) % newtables.size();cur->_next = newtables[newHashi];newtables[newHashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newtables);}// 在当前位置插入新节点Node* newnode = new Node(data);newnode->_next = _tables[hashi];_tables[hashi] = newnode;++_n;return make_pair(Iterator(newnode, this), true);}// 在哈希表中查找数据Iterator Find(const K& key) {KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur) {if (kot(cur->_data) == key) {return Iterator(cur, this);}cur = cur->_next;}return End();}// 从哈希表中删除数据bool Erase(const K& key) {KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* prev = nullptr;Node* cur = _tables[hashi];while (cur) {if (kot(cur->_data) == key) {if (prev == nullptr) {_tables[hashi] = cur->_next;}else {prev->_next = cur->_next;}delete cur;--_n;return true;}prev = cur;cur = cur->_next;}return false;}
private:// 计算下一个素数,用于扩容inline unsigned long __stl_next_prime(unsigned long n) {static const int __stl_num_primes = 28;static const unsigned long __stl_prime_list[__stl_num_primes] = {53, 97, 193, 389, 769,1543, 3079, 6151, 12289, 24593,49157, 98317, 196613, 393241, 786433,1572869, 3145739, 6291469, 12582917, 25165843,50331653, 100663319, 201326611, 402653189, 805306457,1610612741, 3221225473, 4294967291};const unsigned long* first = __stl_prime_list;const unsigned long* last = __stl_prime_list + __stl_num_primes;const unsigned long* pos = lower_bound(first, last, n);return pos == last? *(last - 1) : *pos;}// 哈希表存储数据的桶,是一个指针数组vector<Node*> _tables;// 哈希表中存储数据的个数size_t _n = 0;
};
}
1.1 迭代器相关函数
Iterator Begin()
Iterator Begin() {if (_n == 0) {return End();}for (size_t i = 0; i < _tables.size(); ++i) {Node* cur = _tables[i];if (cur) {return Iterator(cur, this);}}return End();
}
  • 功能:返回哈希表起始位置的迭代器。
  • 步骤
    1. 检查哈希表中元素数量 _n 是否为 0,如果是则直接返回表示结束位置的迭代器。
    2. 遍历哈希表的每个桶(_tables),找到第一个不为空的桶。
    3. 如果找到不为空的桶,返回指向该桶第一个节点的迭代器。
    4. 如果遍历完所有桶都没有找到非空桶,则返回表示结束位置的迭代器。
Iterator End()

 

Iterator End() {return Iterator(nullptr, this);
}
  • 功能:返回哈希表结束位置的迭代器,通常用一个指向 nullptr 的迭代器表示。
  • 步骤:创建一个指向 nullptr 的迭代器并返回。
ConstIterator Begin() const 和 ConstIterator End() const

  • 功能:与上述 Begin() 和 End() 类似,但用于常量哈希表,返回常量迭代器。
  • 实现细节:实现逻辑与非常量版本相同,只是返回的是常量迭代器。
1.2 插入函数 Insert
pair<Iterator, bool> Insert(const T& data) {KeyOfT kot;Iterator it = Find(kot(data));if (it != End()) {return make_pair(it, false);}Hash hs;size_t hashi = hs(kot(data)) % _tables.size();if (_n == _tables.size()) {vector<Node*> newtables(__stl_next_prime(_tables.size() + 1), nullptr);for (size_t i = 0; i < _tables.size(); ++i) {Node* cur = _tables[i];while (cur) {Node* next = cur->_next;size_t newHashi = hs(kot(cur->_data)) % newtables.size();cur->_next = newtables[newHashi];newtables[newHashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newtables);}Node* newnode = new Node(data);newnode->_next = _tables[hashi];_tables[hashi] = newnode;++_n;return make_pair(Iterator(newnode, this), true);
}
  • 功能:向哈希表中插入一个新的数据项。
  • 步骤
    1. 使用 Find 函数检查数据是否已经存在于哈希表中,如果存在则返回该元素的迭代器和 false
    2. 计算数据的哈希值,确定其在哈希表中的桶位置 hashi
    3. 检查负载因子(元素数量 _n 与桶数量 _tables.size() 相等),如果达到阈值则进行扩容操作:
      • 创建一个新的更大的哈希表 newtables,桶的数量为下一个素数。
      • 遍历原哈希表的每个桶,将每个节点重新计算哈希值并插入到新的哈希表中。
      • 交换原哈希表和新哈希表的指针。
    4. 创建一个新的节点 newnode,将其插入到对应桶的头部。
    5. 元素数量 _n 加 1。
    6. 返回新插入节点的迭代器和 true
1.3 查找函数 Find

 

Iterator Find(const K& key) {KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur) {if (kot(cur->_data) == key) {return Iterator(cur, this);}cur = cur->_next;}return End();
}
  • 功能:在哈希表中查找指定键 key 对应的数据项。
  • 步骤
    1. 计算键的哈希值,确定其在哈希表中的桶位置 hashi
    2. 遍历该桶对应的链表,检查每个节点的数据是否与键匹配。
    3. 如果找到匹配的节点,则返回指向该节点的迭代器。
    4. 如果遍历完链表都没有找到匹配的节点,则返回表示结束位置的迭代器。
1.4 删除函数 Erase

bool Erase(const K& key) {KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* prev = nullptr;Node* cur = _tables[hashi];while (cur) {if (kot(cur->_data) == key) {if (prev == nullptr) {_tables[hashi] = cur->_next;}else {prev->_next = cur->_next;}delete cur;--_n;return true;}prev = cur;cur = cur->_next;}return false;
}
  • 功能:从哈希表中删除指定键 key 对应的数据项。
  • 步骤
    1. 计算键的哈希值,确定其在哈希表中的桶位置 hashi
    2. 遍历该桶对应的链表,查找键匹配的节点。
    3. 如果找到匹配的节点:
      • 如果该节点是链表的第一个节点,则将桶的头指针指向该节点的下一个节点。
      • 否则,将前一个节点的 _next 指针指向该节点的下一个节点。
      • 删除该节点,并将元素数量 _n 减 1。
      • 返回 true 表示删除成功。
    4. 如果遍历完链表都没有找到匹配的节点,则返回 false 表示删除失败。
1.5 辅助函数 __stl_next_prime

inline unsigned long __stl_next_prime(unsigned long n) {static const int __stl_num_primes = 28;static const unsigned long __stl_prime_list[__stl_num_primes] = {53, 97, 193, 389, 769,1543, 3079, 6151, 12289, 24593,49157, 98317, 196613, 393241, 786433,1572869, 3145739, 6291469, 12582917, 25165843,50331653, 100663319, 201326611, 402653189, 805306457,1610612741, 3221225473, 4294967291};const unsigned long* first = __stl_prime_list;const unsigned long* last = __stl_prime_list + __stl_num_primes;const unsigned long* pos = lower_bound(first, last, n);return pos == last? *(last - 1) : *pos;
}
  • 功能:计算大于等于 n 的下一个素数,用于哈希表扩容时确定新的桶数量。
  • 步骤
    1. 定义一个素数列表 __stl_prime_list
    2. 使用 lower_bound 函数在素数列表中查找第一个大于等于 n 的素数。
    3. 如果找到则返回该素数,否则返回素数列表中的最后一个素数。

为了实现哈希表的迭代器功能,定义迭代器类HTIterator

2. HTIterator 类 

namespace zdf {
template<class K, class T, class Ptr, class Ref, class KeyOfT, class Hash>
struct HTIterator {// 定义节点类型typedef HashNode<T> Node;// 定义自身类型typedef HTIterator<K, T, Ptr, Ref, KeyOfT, Hash> Self;Node* _node;const HashTable<K, T, KeyOfT, Hash>* _pht;// 构造函数,初始化节点指针和哈希表指针HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht) : _node(node), _pht(pht) {}// 解引用操作符重载,返回节点数据的引用Ref operator*() {return _node->_data;}// 箭头操作符重载,返回节点数据的指针Ptr operator->() {return &_node->_data;}// 不等于操作符重载,用于比较两个迭代器bool operator!=(const Self& s) {return _node != s._node;}// 前置递增操作符重载,移动到下一个节点Self& operator++() {if (_node->_next) {_node = _node->_next;}else {KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();++hashi;while (hashi < _pht->_tables.size()) {if (_pht->_tables[hashi]) {break;}++hashi;}if (hashi == _pht->_tables.size()) {_node = nullptr;}else {_node = _pht->_tables[hashi];}}return *this;}
};
}
2.1 构造函数 HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht)

 

HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht) : _node(node), _pht(pht) {}

  • 功能:初始化迭代器,将节点指针 node 和哈希表指针 pht 赋值给成员变量。
  • 参数
    • node:指向当前节点的指针。
    • pht:指向哈希表的指针。
2.2 解引用操作符 operator*()

Ref operator*() {return _node->_data;
}
  • 功能:返回当前迭代器指向节点的数据的引用。
  • 步骤:直接返回节点的数据成员 _data
2.3 箭头操作符 operator->()

Ptr operator->() {return &_node->_data;
}
  • 功能:返回当前迭代器指向节点的数据的指针。
  • 步骤:返回节点数据成员 _data 的地址。
2.4 不等于操作符 operator!=

 

bool operator!=(const Self& s) {return _node != s._node;
}
  • 功能:比较两个迭代器是否不相等。
  • 步骤:比较两个迭代器指向的节点指针是否不同。
2.5 前置递增操作符 operator++()

Self& operator++() {if (_node->_next) {_node = _node->_next;}else {KeyOfT kot;Hash hs;size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();++hashi;while (hashi < _pht->_tables.size()) {if (_pht->_tables[hashi]) {break;}++hashi;}if (hashi == _pht->_tables.size()) {_node = nullptr;}else {_node = _pht->_tables[hashi];}}return *this;
}
  • 功能:将迭代器移动到下一个节点。
  • 步骤
    1. 如果当前节点有下一个节点,则直接将迭代器指向该下一个节点。
    2. 如果当前节点没有下一个节点,则需要找到下一个非空的桶:
      • 计算当前节点所在的桶位置 hashi
      • 从下一个桶开始查找,直到找到一个非空的桶。
      • 如果遍历完所有桶都没有找到非空桶,则将迭代器指向 nullptr
      • 否则,将迭代器指向该非空桶的第一个节点。
    3. 返回迭代器自身的引用。

 


💯基于哈希表实现 unordered_set

   unordered_set是一个无序的集合容器,它存储唯一的键值。通过复用前面实现的哈希表,定义unordered_set类。

namespace zdf {
template<class K, class Hash = HashFunc<K>>
class unordered_set {// 仿函数,用于从键值中提取键struct SetKeyOfT {const K& operator()(const K& key) {return key;}};
public:// 定义迭代器类型typedef typename HashTable<K, const K, SetKeyOfT, Hash>::Iterator iterator;// 定义常量迭代器类型typedef typename HashTable<K, const K, SetKeyOfT, Hash>::ConstIterator const_iterator;// 返回容器起始位置的迭代器iterator begin() {return _ht.Begin();}// 返回容器结束位置的迭代器iterator end() {return _ht.End();}// 返回常量容器起始位置的迭代器const_iterator begin() const {return _ht.Begin();}// 返回常量容器结束位置的迭代器const_iterator end() const {return _ht.End();}// 插入键值到unordered_setpair<iterator, bool> insert(const K& key) {return _ht.Insert(key);}// 在unordered_set中查找键值iterator Find(const K& key) {return _ht.Find(key);}// 从unordered_set中删除键值bool Erase(const K& key) {return _ht.Erase(key);}
private:// 使用哈希表存储数据HashTable<K, const K, SetKeyOfT, Hash> _ht;
};
}

💯基于哈希表实现 unordered_map

   unordered_map是一个无序的键值对容器,它允许通过键快速查找对应的值。同样复用哈希表来实现unordered_map类。

namespace zdf {
template<class K, class V, class Hash = HashFunc<K>>
class unordered_map {// 仿函数,用于从键值对中提取键struct MapKeyOfT {const K& operator()(const pair<K, V>& kv) {return kv.first;}};
public:// 定义迭代器类型typedef typename HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::Iterator iterator;// 定义常量迭代器类型typedef typename HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::ConstIterator const_iterator;// 返回容器起始位置的迭代器iterator begin() {return _ht.Begin();}// 返回容器结束位置的迭代器iterator end() {return _ht.End();}// 返回常量容器起始位置的迭代器const_iterator begin() const {return _ht.Begin();}// 返回常量容器结束位置的迭代器const_iterator end() const {return _ht.End();}// 插入键值对到unordered_mappair<iterator, bool> insert(const pair<K, V>& kv) {return _ht.Insert(kv);}// 通过键访问对应的值,若键不存在则插入默认值V& operator[](const K& key) {pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));return ret.first->second;}// 在unordered_map中查找键值对iterator Find(const K& key) {return _ht.Find(key);}// 从unordered_map中删除键值对bool Erase(const K& key) {return _ht.Erase(key);}
private:// 使用哈希表存储数据HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;
};
}
 operator[] 函数
V& operator[](const K& key) {pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));return ret.first->second;
}
  • 功能:通过键访问对应的值,如果键不存在则插入一个默认值。
  • 步骤
    1. 调用底层哈希表的 Insert 函数插入键值对(如果键不存在)。
    2. 返回插入或已存在的键值对的值的引用。

 


💯测试代码

为了验证自定义的unordered_mapunordered_set的功能正确性,编写测试函数。

using namespace std;
// 测试unordered_set的功能
void test_set() {zdf::unordered_set<int> s;int a[] = { 4,2,6,1,3,5,15,7,16,14,3,3,15 };for (auto e : a) {s.insert(e);}for (auto e : s) {cout << e << " ";}cout << endl;zdf::unordered_set<int>::iterator it = s.begin();while (it != s.end()) {cout << *it << " ";++it;}cout << endl;
}// 测试unordered_map的功能
void test_map() {zdf::unordered_map<string, string> dict;dict.insert({ "sort", "排序" });dict.insert({ "Left", "左边" });dict.insert({ "right", "右边" });dict["left"] = "左边,剩余";dict["insert"] = "插入";dict["string"];zdf::unordered_map<string, string>::iterator it = dict.begin();while (it != dict.end()) {it->second += 'x';cout << it->first << ":" << it->second << endl;++it;}cout << endl;
}int main() {test_set();test_map();return 0;
}

        通过上述代码,我们基于哈希表成功模拟实现了自定义的unordered_mapunordered_set,并对其功能进行了测试验证。这不仅有助于深入理解哈希表的工作原理以及 STL 容器的实现机制,也为在实际开发中根据特定需求进行容器的定制化提供了思路和参考。

 如果文章对你有帮助,欢迎关注我👉【A charmer】

相关文章:

深入理解并实现自定义 unordered_map 和 unordered_set

亲爱的读者朋友们&#x1f603;&#xff0c;此文开启知识盛宴与思想碰撞&#x1f389;。 快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 在 C 的标准模板库&#xff08;STL&#xff09;中&#xff0c;unorder…...

顶顶通呼叫中心中间件(mod_cti基于FreeSWITCH)-大模型电话机器人

语音流直接对接Realtime API 多模态大模型 直接把音频流输出给大模型&#xff0c;大模型返回音频流。 顶顶通CTI对Realtime API 的支持 提供了以下2个APP可对接任意 •cti_audio_stream 通过TCP推流和播放流&#xff0c;适合用于人机对话场景。 •cti_unicast_start 通过旁…...

kinova机械臂绿色灯一闪一闪及刷机方法

一、背景 实验室有两个kinova mico机械臂&#xff0c;但经常出现操纵杆上的绿色灯一闪一闪的&#xff0c;导致无法使用操纵杆或ROS进行控制&#xff0c;下面给出官方的教程以及所需要的FS 0CPP 0008_6.2.5_mico_6dof.hex文件。 重要的东西写在前面&#xff1a; a、如果出现操…...

第16天:C++多线程完全指南 - 从基础到现代并发编程

第16天&#xff1a;C多线程完全指南 - 从基础到现代并发编程 一、多线程基础概念 1. 线程创建与管理&#xff08;C11&#xff09; #include <iostream> #include <thread>void hello() {std::cout << "Hello from thread " << std::this_…...

中科大计算机网络原理 1.5 Internt结构和ISP

一、互联网的层次化架构 ‌覆盖范围分层‌ ‌主干网&#xff08;Tier-1级&#xff09;‌ 国家级或行业级核心网络&#xff0c;承担跨区域数据传输和全球互联功能。例如中国的四大主干网&#xff08;ChinaNET、CERNET等&#xff09;以及跨国运营商&#xff08;如AT&T、Deuts…...

Windows安装sql server2017

看了下官网的文档&#xff0c;似乎只有ubuntu18.04可以安装&#xff0c;其他debian系的都不行&#xff0c;还有通过docker的方式安装的。 双击进入下载的ISO&#xff0c;点击执行可执行文件&#xff0c;并选择“是” 不要勾选 警告而已&#xff0c;不必理会 至少勾选这两…...

计算机网络之传输层(tcp协议)

一、TCP协议的特点 面向连接&#xff1a;TCP使用面向连接的通信模式&#xff0c;通信双方需要先建立连接&#xff0c;然后才能进行数据的传输。连接建立过程采用三次握手的方式。 可靠性&#xff1a;TCP提供可靠的数据传输服务&#xff0c;确保数据的完整性、有序性和正确性。…...

从零到一:如何用阿里云百炼和火山引擎搭建专属 AI 助手(DeepSeek)?

本文首发&#xff1a;从零到一&#xff1a;如何用阿里云百炼和火山引擎搭建专属 AI 助手&#xff08;DeepSeek&#xff09;&#xff1f; 阿里云百炼和火山引擎都推出了免费的 DeepSeek 模型体验额度&#xff0c;今天我和大家一起搭建一个本地的专属 AI 助手。  阿里云百炼为 …...

Open3D解决SceneWidget加入布局中消失的问题

Open3D解决SceneWidget加入布局中消失的问题 Open3D解决SceneWidget加入布局中消失的问题1. 问题2. 问题代码3. 解决 Open3D解决SceneWidget加入布局中消失的问题 1. 问题 把SceneWidget加到布局管理其中图形可以展示出来&#xff0c;但是鼠标点击就消失了。 stackoverflow上已…...

计算机毕业设计Python+DeepSeek-R1大模型游戏推荐系统 Steam游戏推荐系统 游戏可视化 游戏数据分析(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

Linux笔记---缓冲区

1. 什么是缓冲区 在计算机系统中&#xff0c;缓冲区&#xff08;Buffer&#xff09; 是一种临时存储数据的区域&#xff0c;主要用于协调不同速度或不同时序的组件之间的数据传输&#xff0c;以提高效率并减少资源冲突。它是系统设计中的重要概念&#xff0c;尤其在I/O操作、网…...

如何流畅访问github

1.传输数据原理 本地计算机通过本地网接入运营骨干网&#xff0c;经过DNS域名解析&#xff0c;将输入的字符解析为要连接的真实IP地址&#xff0c;服务器返还一个数据包(github)给计算机 2.原因 DNS域名污染-DNS解析出现问题&#xff0c;导致访问一个不存在的服务器 3.解决…...

java基础+面向对象

Java基础语法 CMD命令 cls 清屏 cd 目录进入文件 cd… 退回 dir 查看当前目录所有文件 E&#xff1a;进入E盘 exit 退出 环境变量就是不用去专门的盘符去找&#xff0c;直接去环境变量里找到文件 语言优势 编译型语言c&#xff1a; 整体翻译 解释型语言python&#x…...

Linux 检测内存泄漏方法总结

文章目录 strace检测asan内存检测linux下gperf工具&#xff08;tcmalloc&#xff09;检查C/C代码内存泄露问题参考 strace检测 &#xff08;1&#xff09;启动程序 &#xff08;2&#xff09; strace -f -p <PID> -tt -e brk,mmap,mmap2,munmapbrk 变大 → 说明堆增长…...

本地部署deepseek大模型后使用c# winform调用(可离线)

介于最近deepseek的大火&#xff0c;我就在想能不能用winform也玩一玩本地部署&#xff0c;于是经过查阅资料&#xff0c;然后了解到ollama部署deepseek,最后用ollama sharp NUGet包来实现winform调用ollama 部署的deepseek。 本项目使用Vs2022和.net 8.0开发&#xff0c;ollam…...

Python----数据分析(Numpy:安装,数组创建,切片和索引,数组的属性,数据类型,数组形状,数组的运算,基本函数)

一、 Numpy库简介 1.1、概念 NumPy(Numerical Python)是一个开源的Python科学计算库&#xff0c;旨在为Python提供 高性能的多维数组对象和一系列工具。NumPy数组是Python数据分析的基础&#xff0c;许多 其他的数据处理库&#xff08;如Pandas、SciPy&#xff09;都依赖于Num…...

Leetcode-最大矩形(单调栈)

一、题目描述 给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵&#xff0c;找出只包含 1 的最大矩形&#xff0c;并返回其面积。 输入&#xff1a;matrix [["1","0","1","0","0"],["1","0&…...

域内委派维权

为某个服务账户配置 krbtgt 用户的非约束性委派或基于资源的约束性委派。这里我的 krbtgt 的基于资源约束性委派我利用不了&#xff0c;所以使用的是域控的机器账户 dc01$ 进行维权。 抓取所有 hash。 mimikatz.exe "privilege::debug" "lsadump::dcsync /doma…...

leetcode---LCR 140.训练计划

给定一个头节点为 head 的链表用于记录一系列核心肌群训练项目编号&#xff0c;请查找并返回倒数第 cnt 个训练项目编号。 示例 1&#xff1a; 输入&#xff1a;head [2,4,7,8], cnt 1 输出&#xff1a;8 提示&#xff1a; 1 < head.length < 1000 < head[i] <…...

Linux基础 -- ARM 32位常用机器码(指令)整理

ARM 32位常用机器码&#xff08;指令&#xff09;整理 1. 数据处理指令&#xff08;运算、逻辑、比较&#xff09; 指令含义示例备注MOV赋值&#xff08;寄存器传输&#xff09;MOV R0, R1直接将 R1 复制到 R0MVN取反MVN R0, R1R0 ~R1ADD加法ADD R0, R1, R2R0 R1 R2ADC带进…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

【C++】纯虚函数类外可以写实现吗?

1. 答案 先说答案&#xff0c;可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...