深入理解并实现自定义 unordered_map 和 unordered_set
亲爱的读者朋友们😃,此文开启知识盛宴与思想碰撞🎉。
快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。
在 C++ 的标准模板库(STL)中,unordered_map
和unordered_set
是非常实用的容器,它们基于哈希表实现,能够提供高效的查找、插入和删除操作。在 SGI - STL30 版本中,虽然没有标准的unordered_map
和unordered_set
,但有类似功能的hash_map
和hash_set
,它们作为非标准容器存在。
本文将基于哈希表来模拟实现自定义的unordered_map
和unordered_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();
}
- 功能:返回哈希表起始位置的迭代器。
- 步骤:
- 检查哈希表中元素数量
_n
是否为 0,如果是则直接返回表示结束位置的迭代器。- 遍历哈希表的每个桶(
_tables
),找到第一个不为空的桶。- 如果找到不为空的桶,返回指向该桶第一个节点的迭代器。
- 如果遍历完所有桶都没有找到非空桶,则返回表示结束位置的迭代器。
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);
}
- 功能:向哈希表中插入一个新的数据项。
- 步骤:
- 使用
Find
函数检查数据是否已经存在于哈希表中,如果存在则返回该元素的迭代器和false
。- 计算数据的哈希值,确定其在哈希表中的桶位置
hashi
。- 检查负载因子(元素数量
_n
与桶数量_tables.size()
相等),如果达到阈值则进行扩容操作:
- 创建一个新的更大的哈希表
newtables
,桶的数量为下一个素数。- 遍历原哈希表的每个桶,将每个节点重新计算哈希值并插入到新的哈希表中。
- 交换原哈希表和新哈希表的指针。
- 创建一个新的节点
newnode
,将其插入到对应桶的头部。- 元素数量
_n
加 1。- 返回新插入节点的迭代器和
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
对应的数据项。- 步骤:
- 计算键的哈希值,确定其在哈希表中的桶位置
hashi
。- 遍历该桶对应的链表,检查每个节点的数据是否与键匹配。
- 如果找到匹配的节点,则返回指向该节点的迭代器。
- 如果遍历完链表都没有找到匹配的节点,则返回表示结束位置的迭代器。
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
对应的数据项。- 步骤:
- 计算键的哈希值,确定其在哈希表中的桶位置
hashi
。- 遍历该桶对应的链表,查找键匹配的节点。
- 如果找到匹配的节点:
- 如果该节点是链表的第一个节点,则将桶的头指针指向该节点的下一个节点。
- 否则,将前一个节点的
_next
指针指向该节点的下一个节点。- 删除该节点,并将元素数量
_n
减 1。- 返回
true
表示删除成功。- 如果遍历完链表都没有找到匹配的节点,则返回
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
的下一个素数,用于哈希表扩容时确定新的桶数量。- 步骤:
- 定义一个素数列表
__stl_prime_list
。- 使用
lower_bound
函数在素数列表中查找第一个大于等于n
的素数。- 如果找到则返回该素数,否则返回素数列表中的最后一个素数。
为了实现哈希表的迭代器功能,定义迭代器类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;
}
- 功能:将迭代器移动到下一个节点。
- 步骤:
- 如果当前节点有下一个节点,则直接将迭代器指向该下一个节点。
- 如果当前节点没有下一个节点,则需要找到下一个非空的桶:
- 计算当前节点所在的桶位置
hashi
。- 从下一个桶开始查找,直到找到一个非空的桶。
- 如果遍历完所有桶都没有找到非空桶,则将迭代器指向
nullptr
。- 否则,将迭代器指向该非空桶的第一个节点。
- 返回迭代器自身的引用。
💯基于哈希表实现 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;
}
- 功能:通过键访问对应的值,如果键不存在则插入一个默认值。
- 步骤:
- 调用底层哈希表的
Insert
函数插入键值对(如果键不存在)。- 返回插入或已存在的键值对的值的引用。
💯测试代码
为了验证自定义的unordered_map
和unordered_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_map
和unordered_set
,并对其功能进行了测试验证。这不仅有助于深入理解哈希表的工作原理以及 STL 容器的实现机制,也为在实际开发中根据特定需求进行容器的定制化提供了思路和参考。
如果文章对你有帮助,欢迎关注我👉【A charmer】
相关文章:

深入理解并实现自定义 unordered_map 和 unordered_set
亲爱的读者朋友们😃,此文开启知识盛宴与思想碰撞🎉。 快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。 在 C 的标准模板库(STL)中,unorder…...
顶顶通呼叫中心中间件(mod_cti基于FreeSWITCH)-大模型电话机器人
语音流直接对接Realtime API 多模态大模型 直接把音频流输出给大模型,大模型返回音频流。 顶顶通CTI对Realtime API 的支持 提供了以下2个APP可对接任意 •cti_audio_stream 通过TCP推流和播放流,适合用于人机对话场景。 •cti_unicast_start 通过旁…...

kinova机械臂绿色灯一闪一闪及刷机方法
一、背景 实验室有两个kinova mico机械臂,但经常出现操纵杆上的绿色灯一闪一闪的,导致无法使用操纵杆或ROS进行控制,下面给出官方的教程以及所需要的FS 0CPP 0008_6.2.5_mico_6dof.hex文件。 重要的东西写在前面: a、如果出现操…...
第16天:C++多线程完全指南 - 从基础到现代并发编程
第16天:C多线程完全指南 - 从基础到现代并发编程 一、多线程基础概念 1. 线程创建与管理(C11) #include <iostream> #include <thread>void hello() {std::cout << "Hello from thread " << std::this_…...

中科大计算机网络原理 1.5 Internt结构和ISP
一、互联网的层次化架构 覆盖范围分层 主干网(Tier-1级) 国家级或行业级核心网络,承担跨区域数据传输和全球互联功能。例如中国的四大主干网(ChinaNET、CERNET等)以及跨国运营商(如AT&T、Deuts…...

Windows安装sql server2017
看了下官网的文档,似乎只有ubuntu18.04可以安装,其他debian系的都不行,还有通过docker的方式安装的。 双击进入下载的ISO,点击执行可执行文件,并选择“是” 不要勾选 警告而已,不必理会 至少勾选这两…...
计算机网络之传输层(tcp协议)
一、TCP协议的特点 面向连接:TCP使用面向连接的通信模式,通信双方需要先建立连接,然后才能进行数据的传输。连接建立过程采用三次握手的方式。 可靠性:TCP提供可靠的数据传输服务,确保数据的完整性、有序性和正确性。…...

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

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

计算机毕业设计Python+DeepSeek-R1大模型游戏推荐系统 Steam游戏推荐系统 游戏可视化 游戏数据分析(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

Linux笔记---缓冲区
1. 什么是缓冲区 在计算机系统中,缓冲区(Buffer) 是一种临时存储数据的区域,主要用于协调不同速度或不同时序的组件之间的数据传输,以提高效率并减少资源冲突。它是系统设计中的重要概念,尤其在I/O操作、网…...
如何流畅访问github
1.传输数据原理 本地计算机通过本地网接入运营骨干网,经过DNS域名解析,将输入的字符解析为要连接的真实IP地址,服务器返还一个数据包(github)给计算机 2.原因 DNS域名污染-DNS解析出现问题,导致访问一个不存在的服务器 3.解决…...

java基础+面向对象
Java基础语法 CMD命令 cls 清屏 cd 目录进入文件 cd… 退回 dir 查看当前目录所有文件 E:进入E盘 exit 退出 环境变量就是不用去专门的盘符去找,直接去环境变量里找到文件 语言优势 编译型语言c: 整体翻译 解释型语言python&#x…...
Linux 检测内存泄漏方法总结
文章目录 strace检测asan内存检测linux下gperf工具(tcmalloc)检查C/C代码内存泄露问题参考 strace检测 (1)启动程序 (2) strace -f -p <PID> -tt -e brk,mmap,mmap2,munmapbrk 变大 → 说明堆增长…...

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

Python----数据分析(Numpy:安装,数组创建,切片和索引,数组的属性,数据类型,数组形状,数组的运算,基本函数)
一、 Numpy库简介 1.1、概念 NumPy(Numerical Python)是一个开源的Python科学计算库,旨在为Python提供 高性能的多维数组对象和一系列工具。NumPy数组是Python数据分析的基础,许多 其他的数据处理库(如Pandas、SciPy)都依赖于Num…...

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

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

leetcode---LCR 140.训练计划
给定一个头节点为 head 的链表用于记录一系列核心肌群训练项目编号,请查找并返回倒数第 cnt 个训练项目编号。 示例 1: 输入:head [2,4,7,8], cnt 1 输出:8 提示: 1 < head.length < 1000 < head[i] <…...
Linux基础 -- ARM 32位常用机器码(指令)整理
ARM 32位常用机器码(指令)整理 1. 数据处理指令(运算、逻辑、比较) 指令含义示例备注MOV赋值(寄存器传输)MOV R0, R1直接将 R1 复制到 R0MVN取反MVN R0, R1R0 ~R1ADD加法ADD R0, R1, R2R0 R1 R2ADC带进…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...