21.使用哈希桶封装unordered_set和unordered_map(哈希桶的迭代器的实现)

1. hash表迭代器的实现

1.1 普通迭代器

// 由于迭代器的实现,需要使用哈希桶,但是哈希桶的实现,又在迭代器之后
// 因此,我们给出一个前置声明,旨在告诉系统,哈希桶已经被实现了
// 前置声明
template<class K, class T, class Hash, class KeyOfT>
class HashTable;// 迭代器的类模板
template<class K, class T, class Hash, class KeyOfT>
struct __HTIterator
{// 将节点的类模板类型重新定义为 Nodetypedef HashNode<T> Node;// 将迭代器的类模板类型重新定义为Selftypedef __HTIterator<K, T, Hash, KeyOfT> Self;// 将哈希桶的类模板类型重新定义为HTtypedef HashTable<K, T, Hash, KeyOfT> HT;// 创建一个节点// 创建一个哈希桶Node* _node;HT* _ht;// 使用构造函数来构造迭代器,初始化节点和哈希桶(必须传参的构造函数)__HTIterator(Node* node, HT* ht):_node(node), _ht(ht){}// 解引用T& operator*(){// 解引用拿到data,并引用返回return _node->_data;}// -> 重载T* operator->(){// ->重载也就是返回data的地址return &_node->_data;}bool operator != (const Self& s) const{// _node是节点的地址// 此处,判断不等,直接判断两个迭代器节点的地址是否不等就可以了return _node != s._node;}// ++的大逻辑就是,通过vector找到每一个桶的头节点,再通过桶的头节点来遍历每一个桶的所有节点Self& operator++(){if (_node->_next){// 如果说_node->_next不为空,说明当前这个桶的节点还没有全部遍历完,// 因此我们接着向后遍历就可以了_node = _node->_next;}else{// 代码运行到这里,说明当前桶走完了,要找下一个桶的第一个节点// 找下一个桶的头节点的位置,就需要知道hashi,想要直到hashi// 就需要使用仿函数KeyofT来拿到键值对中的key值KeyOfT kot;// hash函数对象,可以将key转化为整型Hash hash;// hashi = key % 哈希桶的个数  (必须先用hash函数将key转化为整型)// 这里的_ht->_tables.size(),使用了哈希桶的私有成员函数,因此在哈希桶的类模板中,迭代器的类模板是其友元类size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();// 拿到当前桶的头节点对应的hashi,++之后就是下一个桶头节点存储的下标(hashi)++hashi;// ++之后的hashi,我们要确保其没有越界// 也就是保证hashi < _ht->_tables.size()while (hashi < _ht->_tables.size()){// 如果hashi没有越界// 那么我们要判断,当前hashi所在的下标位置是否为空// 如果不为空,_ht->_tables[hashi]存放的就是对应桶的头节点,这就是++后指向的节点,我们将其给到_node// 如果_ht->_tables[hashi]为空,说明vector当前位置不存在桶,那么我们接着++hashi// 直到这个下标位置有桶,也就是_ht->_tables[hashi]不为空,则停止++hashiif (_ht->_tables[hashi]){_node = _ht->_tables[hashi];break;}else{++hashi;}}// 说明后面没有桶了,那么++后指向的就是空节点if (hashi == _ht->_tables.size())_node = nullptr;}return *this;}
};

1.2 const_iterator

// 对于哈希桶,const迭代器不可以和普通迭代器共用一个类来实现
// 迭代器
// 前置声明
template<class K, class T, class Hash, class KeyOfT>
class HashTable;// 为什么const迭代器没有复用?
template<class K, class T, class Ref, class Ptr, class Hash, class KeyOfT>
struct __HTIterator
{typedef HashNode<T> Node;// 将typedef __HTIterator<K, T, Ref, Ptr, Hash, KeyOfT> Self;typedef HashTable<K, T, Hash, KeyOfT> HT;const Node* _node;const HT* _ht;// 此时迭代器的构造函数必须被const修饰// 才可以构造const迭代器的begin()// 但是初始化_node时,_node也必须被const修饰,否则node赋值给_node之后,权限会被放大// 因此_node也必须被const修饰__HTIterator(const Node* node, const HT* ht):_node(node), _ht(ht){}// 但是如果_node和_ht都被const修饰// 且构造函数的参数都被const修饰,// 那么普通迭代器调用时最终返回的 Ref是T&,Ptr是T*// 但是我们返回的_node->_data和&_node->_data却都被const修饰// 因此,我们必须把普通迭代器和const迭代器分开写,不可以进行复用Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator != (const Self& s) const{return _node != s._node;}Self& operator++(){if (_node->_next){_node = _node->_next;}else{// 当前桶走完了,要找下一个桶的第一个KeyOfT kot;Hash hash;size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();++hashi;while (hashi < _ht->_tables.size()){if (_ht->_tables[hashi]){_node = _ht->_tables[hashi];break;}else{++hashi;}}// 后面没有桶了if (hashi == _ht->_tables.size())_node = nullptr;}return *this;}
};

2.哈希桶的改造

因为我们要实现的unordered_set和unordered_map的底层都是同一个hash桶的头文件,因此我们需要对hash桶做出一些改造。

哈希桶的类模板的改造

namespace buckethash
{/*因为哈希桶是作为unordered_set和unordered_map的底层,因此数据类型,我们设置为模板T,这样就可以适用于unordered_set和unordered_map传递的不同的参数*/// 哈希桶存放的节点的类模板template<class T>struct HashNode{// pair<K, V> _kv;// T可以实例化为键值对,也可以是其他类型的数据T _data;HashNode<T>* _next;HashNode(const T& data):_data(data), _next(nullptr){}};// 哈希桶的类模板template<class K, class T, class Hash, class KeyOfT>class HashTable{// 将存储的节点的类模板类型重新定义为Nodetypedef HashNode<T> Node;// 在迭代器中 _ht->_tables.size() 这个调用了哈希桶的私有成员_tables// 由于迭代器使用了哈希桶的私有成员// 为了使迭代器拥有使用私有成员的权限// 我们将迭代器的类模板设置为哈希桶的友元// 这样迭代器就可以调用哈希桶的私有成员template<class K, class T, class Hash, class KeyOfT>friend struct __HTIterator;public:// 将迭代器的类模板类型重新定义为iteratortypedef __HTIterator<K, T, Hash, KeyOfT> iterator;// 起始位置的迭代器iterator begin(){// 找到第一个节点,并将其构造为迭代器for (size_t i = 0; i < _tables.size(); ++i){// 当_tables[i]不为空时,说明我们找到了第一个桶的头节点,// 而_tables[i]就是第一个桶的头节点的地址if (_tables[i]){// 迭代器的构造,需要传递两个参数// 第一个参数是节点的地址,第二个参数是哈希桶的地址(指针)// 我们创建一个迭代器的匿名对象,这个传递的效率更高return iterator(_tables[i], this);}}// 如果说这个哈希表是没有数据的,那么_tables[i]始终为空// 则我们直接返回使用空指针和哈希桶指针构造的迭代器return iterator(nullptr, this);}// 结束位置的迭代器iterator end(){// 结束的迭代器,是使用空指针和哈希桶的指针来构造的// 为什么是空指针呢,因为结束的迭代器指向的不是节点,而是空指针return iterator(nullptr, this);}// 哈希桶的无参构造函数HashTable():_n(0){//_tables.resize(10);_tables.resize(__stl_next_prime(0));}// 哈希桶的析构函数~HashTable(){for (size_t i = 0; i < _tables.size(); ++i){// 释放桶Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}// 插入数据data,并返回一个键值对函数对象,first存放的是指向data的迭代器,second代表插入成功pair<iterator, bool> Insert(const T& data){// 用KeyofT来获取key元素KeyOfT kot;// 查到key元素,如果找到则返回key对应节点的对应的迭代器// 当查到到,那么我们就不需要插入key// 如果it != end()为真,说明key已经存在,我们直接返回make_pair(it, false)// pair由迭代器和bool构造,it就是指向key的迭代器// false说明key已经存在,我们不可以再进行插入了,代表了插入失败iterator it = Find(kot(data));if (it != end())return make_pair(it, false);// 此时说明key不存在,那么就将key进行插入// 负载因子控制在1,超过就扩容if (_tables.size() == _n){vector<Node*> newTables;newTables.resize(__stl_next_prime(_tables.size()), nullptr);for (size_t i = 0; i < _tables.size(); ++i){Node* cur = _tables[i];while (cur){Node* next = cur->_next;size_t hashi = Hash()(kot(cur->_data)) % newTables.size();// 头插到新表cur->_next = newTables[hashi];newTables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newTables);}size_t hashi = Hash()(kot(data)) % _tables.size();// 头插Node* newnode = new Node(data);newnode->_next = _tables[hashi];_tables[hashi] = newnode;++_n;return  make_pair(iterator(newnode, this), true);}// 查找到key对应的节点,返回这个节点的迭代器iterator Find(const K& key){KeyOfT kot;// 找到key对应的hashisize_t hashi = Hash()(key) % _tables.size();// 拿到hashi这个下标对应的桶的头节点Node* cur = _tables[hashi];// 依次遍历整个桶的节点,直到cur为空while (cur){// 用KeyofT来获取T中的keyif (kot(cur->_data) == key){// 如果两个key相等,则返回对应节点的迭代器return iterator(cur, this);}else{// 当两个节点的key元素不相等,则我们接着遍历cur节点的下一个节点cur = cur->_next;}}// 如果代码运行到这里,说明没有对应的节点,则我们直接返回尾迭代器// 尾迭代器由空指针(也就是没有对应的节点,我们已经遍历到了空指针了),和迭代器的指针构造return end();}// 删除key对应的节点bool Erase(const K& key){size_t hashi = Hash()(key) % _tables.size();Node* prev = nullptr;Node* cur = _tables[hashi];while (cur){if (cur->_kv.first == key){// 准备删除if (cur == _tables[hashi]){_tables[hashi] = cur->_next;}else{prev->_next = cur->_next;}delete cur;--_n;return true;}else{prev = cur;cur = cur->_next;}}return false;}// 素数表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};for (int i = 0; i < __stl_num_primes; ++i){if (__stl_prime_list[i] > n){return __stl_prime_list[i];}}return __stl_prime_list[__stl_num_primes - 1];}private:vector<Node*> _tables;  // 指针数组(存放哈希桶的头节点)size_t _n = 0;};}

哈希桶的完整实现

namespace buckethash
{// 哈希桶节点的类模板template<class T>struct HashNode{T _data;HashNode<T>* _next;HashNode(const T& data):_data(data), _next(nullptr){}};// 前置声明(说明哈希桶已经实现了)template<class K, class T, class Hash, class KeyOfT>class HashTable;// 哈希桶迭代器的类模板template<class K, class T, class Hash, class KeyOfT>struct __HTIterator{typedef HashNode<T> Node;typedef __HTIterator<K, T, Hash, KeyOfT> Self;typedef HashTable<K, T, Hash, KeyOfT> HT;Node* _node;HT* _ht;__HTIterator(Node* node, HT* ht):_node(node), _ht(ht){}T& operator*(){return _node->_data;}T* operator->(){return &_node->_data;}bool operator != (const Self& s) const{return _node != s._node;}Self& operator++(){if (_node->_next){_node = _node->_next;}else{// 当前桶走完了,要找下一个桶的第一个KeyOfT kot;Hash hash;size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();++hashi;while (hashi < _ht->_tables.size()){if (_ht->_tables[hashi]){_node = _ht->_tables[hashi];break;}else{++hashi;}}// 后面没有桶了if (hashi == _ht->_tables.size())_node = nullptr;}return *this;}};// 哈希桶的类模板template<class K, class T, class Hash, class KeyOfT>class HashTable{// 将哈希桶的节点的类模板类型定义为Nodetypedef HashNode<T> Node;template<class K, class T, class Hash, class KeyOfT>friend struct __HTIterator;public:typedef __HTIterator<K, T, Hash, KeyOfT> iterator;iterator begin(){for (size_t i = 0; i < _tables.size(); ++i){if (_tables[i]){return iterator(_tables[i], this);}}return iterator(nullptr, this);}iterator end(){return iterator(nullptr, this);}HashTable():_n(0){_tables.resize(__stl_next_prime(0));}~HashTable(){for (size_t i = 0; i < _tables.size(); ++i){// 释放桶Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}pair<iterator, bool> Insert(const T& data){KeyOfT kot;iterator it = Find(kot(data));if (it != end())return make_pair(it, false);// 负载因子控制在1,超过就扩容if (_tables.size() == _n){vector<Node*> newTables;newTables.resize(__stl_next_prime(_tables.size()), nullptr);for (size_t i = 0; i < _tables.size(); ++i){Node* cur = _tables[i];while (cur){Node* next = cur->_next;size_t hashi = Hash()(kot(cur->_data)) % newTables.size();// 头插到新表cur->_next = newTables[hashi];newTables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newTables);}size_t hashi = Hash()(kot(data)) % _tables.size();// 头插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;size_t hashi = Hash()(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur, this);}else{cur = cur->_next;}}return end();}bool Erase(const K& key){size_t hashi = Hash()(key) % _tables.size();Node* prev = nullptr;Node* cur = _tables[hashi];while (cur){if (cur->_kv.first == key){// 准备删除if (cur == _tables[hashi]){_tables[hashi] = cur->_next;}else{prev->_next = cur->_next;}delete cur;--_n;return true;}else{prev = cur;cur = cur->_next;}}return false;}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};for (int i = 0; i < __stl_num_primes; ++i){if (__stl_prime_list[i] > n){return __stl_prime_list[i];}}return __stl_prime_list[__stl_num_primes - 1];}private:vector<Node*> _tables;  // 指针数组size_t _n = 0;};
}

3.unordered_set的模拟实现

#include "HashTable.h"namespace qwy
{template<class K, class Hash = HashFunc<K>>class unordered_set{//  传递给unordered_set的第一个参数类型就为K,因此仿函数直接返回K类型的值就可以struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:// typename的作用// 告诉编译器buckethash::HashTable<K, K, Hash, SetKeyOfT>::iterator是一个类型名// 而不是一个变量名typedef typename buckethash::HashTable<K, K, Hash, SetKeyOfT>::iterator iterator;iterator begin(){// 底层调用哈希桶的begin()return _ht.begin();}iterator end(){// 底层调用哈希桶的end()return _ht.end();}pair<iterator, bool> insert(const K& key){// 底层调用哈希桶的insert()return _ht.Insert(key);}private:// hash桶对象// template<class K, class T, class Hash, class KeyOfT>// template<class K, class Hash = HashFunc<K>>// 第一个参数和第二个参数,都是用K进行实例化,传入缺省的HashFunc<K>函数,传入仿函数SetKeyOfT,取key值buckethash::HashTable<K, K, Hash, SetKeyOfT> _ht;};void test_unordered_set(){unordered_set<int> us;us.insert(13);us.insert(3);us.insert(23);us.insert(5);us.insert(5);us.insert(6);us.insert(15);us.insert(223342);us.insert(22);unordered_set<int>::iterator it = us.begin();while (it != us.end()){cout << *it << " ";++it;}cout << endl;for (auto e : us){cout << e << " ";}cout << endl;}
}

4.unordered_map的模拟实现

#include "HashTable.h"namespace qwy
{template<class K, class V, class Hash = HashFunc<K>>class unordered_map{// 传递给unordered_map的第二个参数的类型为V,也就是作为pair<const K, V>,的第二个参数// 因此我们取pair::first也就是取pair<const K, V>中的K类型的数据struct MapKeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public:// typename的作用// 告诉编译器buckethash::HashTable<K, K, Hash, SetKeyOfT>::iterator是一个类型名// 而不是一个变量名typedef typename buckethash::HashTable<K, pair<const K, V>, Hash, MapKeyOfT>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}pair<iterator, bool> insert(const pair<K, V>& data){return _ht.Insert(data);}V& operator[](const K& key){// insert的返回类型为pair<iterator, bool>// make_pair(key, V()) 其中V是根据传递的参数类型进行默认构造的// 使用insert(),如果key已经存在,则不会进行插入pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));// ret.first就是迭代器// 迭代器重载->,拿到的是_node->data的地址,也就是键值对的对象的地址// 所以 ret.first->second 的类型就是V,或者称为value// 所以 operator[]的返回值其实就是value的引用return ret.first->second;}private:// 私有成员为哈希桶// template<class K, class V, class Hash = HashFunc<K>>// template<class K, class T, class Hash, class KeyOfT>buckethash::HashTable<K, pair<const K, V>, Hash, MapKeyOfT> _ht;};void test_unordered_map(){string arr[] = { "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };unordered_map<string, int> countMap;for (auto& e : arr){countMap[e]++;}for (const auto& kv : countMap){cout << kv.first << ":" << kv.second << endl;}}
}

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

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

相关文章

产品经理应该懂业务吗?

最近看到一个对新一代产品经理的定义&#xff1a;懂技术、擅运营、会管理、懂业务、能决策 第一反应是&#xff1a;哇偶&#xff0c;产品经理更牛掰了~~~ 说真的&#xff0c;产品经理岗位发展这么多年&#xff0c;还把产品经理定位在十项全能 不是进步&#xff0c;而是倒退。…

Aigtek射频功率放大器的主要功能是什么

射频功率放大器是一种专门用于放大射频信号功率的电子器件&#xff0c;它在无线通信、雷达系统、卫星通信等射频应用中起着至关重要的作用。下面将详细介绍射频功率放大器的主要功能。 信号放大&#xff1a;射频功率放大器的主要功能之一就是将传入的射频信号放大到所需的功率水…

Flask与HTTP

一、请求响应循环 “请求-响应循环”&#xff1a;客户端发出请求&#xff0c;服务器处理请求并返回响应。 Flask Web程序的工作流程&#xff1a; 当用户访问一个URL&#xff0c;浏览器便生成对应的HTTP请求&#xff0c;经由互联网发送到对应的Web服务器。Web服务器接收请求&a…

pandas学习笔记13

map,apply,applymap ** apply&#xff1a;应用在DataFrame的行或列中&#xff1b; applymap&#xff1a;应用在DataFrame的每个元素中&#xff1b; map&#xff1a;应用在单独一列&#xff08;Series&#xff09;的每个元素中。** apply()方法 前面也说了apply方法是一般性的…

【计算机网络】FTP站点配置搭建教程以及相关问题解决方案(超详细)

文章目录 1、安装Window Server 20082、搭建FTP环境&#xff08;1&#xff09;安装FTP服务器&#xff08;2&#xff09;配置FTP服务器&#xff08;3&#xff09;测试FTP连接 3、遇到的问题以及解决方案&#xff08;1&#xff09;Windows无法访问此文件夹&#xff08;2&#xff…

上传文件到 linux

一、mac 法一&#xff1a;scp 先进入mac的 Node_exporter文件&#xff08;要上传的文件&#xff09;目录下 输入scp -P 端口号 文件名 rootIP:/存放路径 scp -P 22 node_exporter-1.8.0.linux-amd64.tar.gz root192.***.2:/root 法二、 rz mac 安装 lrzsz&#xff0c;然后…

dp 动态规划 力扣

64. 最小路径和 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 示例 1&#xff1a; 输入&#xff1a;grid [[1,3,1],[1,5,1],[4,2,1]] 输…

Python基础学习之logging模块

在Python编程中&#xff0c;日志记录&#xff08;Logging&#xff09;是一个非常重要的功能。它不仅可以帮助我们追踪和调试代码中的错误&#xff0c;还可以记录程序运行时的关键信息&#xff0c;以便后续分析和优化。Python标准库中的logging模块为我们提供了强大的日志记录功…

AI日报|gpt2-chatbot神秘聊天机器人引热议,疑似GPT-4.5?《金融时报》与OpenAI达成战略合作...

欢迎大家在 GitHub 上 Star 我们&#xff1a; 分布式全链路因果学习系统 OpenASCE: https://github.com/Open-All-Scale-Causal-Engine/OpenASCE 大模型驱动的知识图谱 OpenSPG: https://github.com/OpenSPG/openspg 大规模图学习系统 OpenAGL: https://github.com/TuGraph-…

踏准芯片定制风口的灿芯股份,护城河足够深吗?

近年来&#xff0c;芯片定制渐成风潮&#xff0c;不仅位于下游、自身有巨大芯片需求的科技巨头如谷歌、OpenAI等纷纷转向定制&#xff0c;而且产业中游主打标准化芯片的主流芯片设计公司如博通、英伟达等&#xff0c;也相继开辟或加码定制业务。 风潮背后&#xff0c;一方面是…

老人摔倒监测识别摄像机

随着社会老龄化程度的不断加深&#xff0c;老年人的健康和安全问题日益凸显。在家中独居的老人&#xff0c;一旦发生意外摔倒等情况&#xff0c;往往难以及时得到帮助&#xff0c;造成了严重的安全隐患。为了解决这一问题&#xff0c;近年来&#xff0c;老人摔倒监测识别摄像机…

AI大模型探索之路-训练篇7:大语言模型Transformer库之HuggingFace介绍

系列篇章&#x1f4a5; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据集概…

鸿蒙组件样式复用简介

鸿蒙组件样式复用简介 使用Style进行复用在Component内部复用在Component外部复用使用Extend复用指定类型组件Extend支持参数传递 使用Style进行复用 在页面开发过程中&#xff0c;会遇到多个组件都在使用相同的样式&#xff0c;这时候就要考虑是不是可以将相同的样式的进行复…

vue 使用 html2canvas 截屏并下载图片至本地(重点兼容ie浏览器)

安装html2canvas&#xff1a; npm install --save html2canvas注意&#xff1a;如果是某些原因不能npm的话就直接把依赖包拷贝到node_modules就可以啦 html&#xff1a; <div class"details-box" v-show"detailsFormShow"><div class"det…

群狼调研(长沙市场调研)|食品安全群众满意度调查方案

本文由群狼调研&#xff08;长沙食品安全满意度调查&#xff09;出品&#xff0c;欢迎转载&#xff0c;请注明出处。设计一个食品安全群众满意度调查方案需要考虑调查目的、样本选择、问题设计、数据收集和分析等多个方面。以下是一个可能的食品安全群众满意度调查方案的概述&a…

AP510X 单路低压差线性恒流芯片 LED手电筒景观亮化台灯车灯照明

AP510X 是一系列电路简洁的单路线性LED恒流芯片&#xff0c;适用于3-60V电压范围的LED恒流调光领域。AP510X采用我司算法&#xff0c;可以实现高精度的恒流效果&#xff0c;输出电流恒流精度≤3&#xff05;&#xff0c;电源供电工作范围为3-40V&#xff0c;可以轻松满足锂电池…

【深度优先搜索 图论 树】2872. 可以被 K 整除连通块的最大数目

本文涉及知识点 深度优先搜索 图论 树 图论知识汇总 LeetCode 2872. 可以被 K 整除连通块的最大数目 给你一棵 n 个节点的无向树&#xff0c;节点编号为 0 到 n - 1 。给你整数 n 和一个长度为 n - 1 的二维整数数组 edges &#xff0c;其中 edges[i] [ai, bi] 表示树中节点…

VMware 虚拟机打开一段时间后卡死,VNX进程CPU占比高

一、问题描述 打开虚拟机后可以正常运行 运行几分钟后突然卡死 然后通过任务管理器可以观察到VMware Workstation VMX应用进程的CPU占比高&#xff0c;CPU也出现异常 关闭虚拟机重新开启&#xff0c;还是一样卡死 二、系统环境 系统: Windows10 VMware: Workstation 17 Pro …

奇门辅助软件v2024.5

废话不说&#xff0c;先上链接 链接&#xff1a;https://pan.baidu.com/s/1_i11lMx4P_vrTs-6lpWoHA?pwd8v1m 提取码&#xff1a;8v1m 功能介绍 【宫内信息】是点击宫内某属性时显示的宫内基本信息。 【古籍宝鉴】是《御定奇门宝鉴》里的对应时局内容&#xff0c;但差补法置…

[Flutter]单元测试和组件测试

1.单元测试 在Flutter开发中&#xff0c;进行单元测试是确保你的代码质量以及未来更改不会破坏现有功能的关键步骤。当你开发私有包时&#xff0c;单元测试尤其重要&#xff0c;因为这有助于保持包的稳定性和可维护性。以下是如何在Flutter中进行单元测试的详细指南&#xff1…