【C++】哈希表的模拟实现及 unordered_set 和 unorderded_map 的封装

在这里插入图片描述

目录

  • 前言
  • 一、哈希表的模拟实现
    • 1.1 哈希表的改造
      • 1.1.1 模板参数列表的改造
      • 1.1.2 增加迭代器操作
    • 1.2 哈希表的模拟实现
      • 1.2.1 哈希表中仿函数的实现
      • 1.2.2 哈希表中节点类的实现
      • 1.2.3 哈希表中迭代器类的实现
      • 1.2.4 哈希表中构造函数、析构函数和 Clear() 函数的实现
      • 1.2.5 哈希表中Swap 和 Size 函数的实现
      • 1.2.6 哈希表中 begin 和 end 函数的实现
      • 1.2.7 哈希表中 Find 函数的实现
      • 1.2.8 哈希表中 Insert 和 Erase 函数的实现
      • 1.2.9 哈希表的整体实现
  • 二、unordered_set的实现及测试
  • 三、unordered_map的实现及测试
  • 结尾

前言

上一篇文章中讲到了哈希的概念和STL中关于哈希容器的使用,并且在后面对开散列进行了模拟实现,那么在这一篇文章中在开散列的基础上进行改造成哈希表,并且在哈希表的基础上封装 unordered_set 和 unordered_map。


一、哈希表的模拟实现

1.1 哈希表的改造

1.1.1 模板参数列表的改造

K:表示关键码类型

T:不同容器V的类型不同,如果是unordered_map,K代表一个键值对,如果是unordered_set,T 为 K。

KOfT: 因为V的类型不同,通过value取key的方式就不同,详细见
unordered_map/set的实现

HF: 哈希函数仿函数对象类型,哈希函数使用除留余数法,需要将Key转换为整形数字才能取模

template<class K, class T ,  class KOfT , class HF>
class hash_table;

1.1.2 增加迭代器操作

// 前置声明,否则后面的__HTIterator找不到hash_tabletemplate<class K, class T, class KOfT, class HF>class hash_table;template<class K, class T , class Ref, class Ptr, class KOfT, class HF>struct __HTIterator{typedef HashNode<T> Node;typedef __HTIterator<K, T, Ref , Ptr , KOfT, HF> self;// 成员变量Node* _node;// 由于const迭代器中const修饰this// 所以这里需要加const,否则会导致权限放大// 编译无法通过的情况const hash_table<K, T, KOfT, HF>* _pht;// size_t hashi;// 构造函数__HTIterator(Node* node, hash_table<K, T, KOfT, HF>* pht , size_t i):_node(node),_pht(pht),hashi(i){}// 构造函数__HTIterator(Node* node, const hash_table<K, T, KOfT, HF>* pht, size_t i):_node(node), _pht(pht), hashi(i){}self& operator++(){if (_node->_next){// 若当前桶其他节点没有走完// 那么继续走下一个节点_node = _node->_next;}else{// 若当前桶的所有节点都已经走完// 那么继续向下一个桶不为空的桶走hashi++;while (hashi < _pht->_table.size()){if (_pht->_table[hashi]){_node = _pht->_table[hashi];break;}hashi++;}if (hashi == _pht->_table.size())_node = nullptr;}return *this;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const self& s){return _node != s._node;}};

1.2 哈希表的模拟实现

1.2.1 哈希表中仿函数的实现

若存储key的类型不是整形时,需要将其转化为整数,整形不需要处理,日常中我们最常用到的类型是string,那么这里就写一个string转化为整形的版本。

// 整数类型
template<class K>
struct HashFunc
{size_t operator()(const K& key){return (size_t)key;}
};// 特化
template<>
struct HashFunc<string>
{size_t operator()(const string& s){size_t sum = 0;for (size_t i = 0; i < s.size(); i++){sum = sum * 31 + s[i];}return sum;}
};

1.2.2 哈希表中节点类的实现

namespace hash_bucket
{template<class T>struct HashNode{HashNode* _next;T _data;HashNode(const T& data):_data(data), _next(nullptr){}};}

1.2.3 哈希表中迭代器类的实现

namespace hash_bucket
{// 前置声明,否则后面的__HTIterator找不到hash_tabletemplate<class K, class T, class KOfT, class HF>class hash_table;template<class K, class T, class Ref, class Ptr, class KOfT, class HF>struct __HTIterator{typedef HashNode<T> Node;typedef __HTIterator<K, T, Ref, Ptr, KOfT, HF> self;// 成员变量Node* _node;// 由于const迭代器中const修饰this// 所以这里需要加const,否则会导致权限放大// 编译无法通过的情况const hash_table<K, T, KOfT, HF>* _pht;// size_t hashi;// 构造函数__HTIterator(Node* node, hash_table<K, T, KOfT, HF>* pht, size_t i):_node(node), _pht(pht), hashi(i){}// 构造函数__HTIterator(Node* node, const hash_table<K, T, KOfT, HF>* pht, size_t i):_node(node), _pht(pht), hashi(i){}self& operator++(){if (_node->_next){// 若当前桶其他节点没有走完// 那么继续走下一个节点_node = _node->_next;}else{// 若当前桶的所有节点都已经走完// 那么继续向下一个桶不为空的桶走hashi++;while (hashi < _pht->_table.size()){if (_pht->_table[hashi]){_node = _pht->_table[hashi];break;}hashi++;}if (hashi == _pht->_table.size())_node = nullptr;}return *this;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const self& s){return _node != s._node;}};
}

1.2.4 哈希表中构造函数、析构函数和 Clear() 函数的实现

namespace hash_bucket
{template<class K, class T, class KOfT, class HF>class hash_table{public:typedef HashNode<T> Node;public:// 构造函数hash_table(int n = 10):_table(vector<Node*>(n)), _n(0){}// 析构函数~hash_table(){Clear();}void Clear(){for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_table[i] = nullptr;}}private:vector<Node*> _table;int _n;};
}

1.2.5 哈希表中Swap 和 Size 函数的实现

namespace hash_bucket
{template<class K, class T, class KOfT, class HF>class hash_table{public:size_t Size(){return _n;}void Swap(hash_table<K, T, KOfT, HF>& ht){_table.swap(ht._table);swap(_n, ht._n);}private:vector<Node*> _table;int _n;};
}

1.2.6 哈希表中 begin 和 end 函数的实现

namespace hash_bucket
{template<class K, class T, class KOfT, class HF>class hash_table{public:typedef HashNode<T> Node;typedef __HTIterator<K, T, T&, T*, KOfT, HF> iterator;typedef __HTIterator<K, T, const T&, const T*, KOfT, HF> const_iterator;// 将__HTIterator设置为hash_table的友元// 使__HTIterator可以访问hash_table的私有template<class K, class T, class Ref, class Ptr, class KOfT, class HF>friend struct __HTIterator;public:// 迭代器iterator begin(){for (size_t i = 0; i < _table.size(); i++){if (_table[i])return iterator(_table[i], this, i);}return end();}iterator end(){return iterator(nullptr, this, -1);}const_iterator begin()const{for (size_t i = 0; i < _table.size(); i++){if (_table[i])return const_iterator(_table[i], this, i);}return end();}const_iterator end()const{return const_iterator(nullptr, this, -1);}private:vector<Node*> _table;int _n;};
}

1.2.7 哈希表中 Find 函数的实现

namespace hash_bucket
{template<class K, class T, class KOfT, class HF>class hash_table{public:typedef HashNode<T> Node;typedef __HTIterator<K, T, T&, T*, KOfT, HF> iterator;typedef __HTIterator<K, T, const T&, const T*, KOfT, HF> const_iterator;// 将__HTIterator设置为hash_table的友元// 使__HTIterator可以访问hash_table的私有template<class K, class T, class Ref, class Ptr, class KOfT, class HF>friend struct __HTIterator;public:// 查找iterator Find(const K& key){HF hf;KOfT koft;int hashi = hf(key) % _table.size();Node* cur = _table[hashi];while (cur){if (koft(cur->_data) == key)return iterator(cur, this, hashi);cur = cur->_next;}// 找不到返回空return end();}private:vector<Node*> _table;int _n;};
}

1.2.8 哈希表中 Insert 和 Erase 函数的实现

namespace hash_bucket
{template<class K, class T, class KOfT, class HF>class hash_table{public:typedef HashNode<T> Node;typedef __HTIterator<K, T, T&, T*, KOfT, HF> iterator;typedef __HTIterator<K, T, const T&, const T*, KOfT, HF> const_iterator;// 将__HTIterator设置为hash_table的友元// 使__HTIterator可以访问hash_table的私有template<class K, class T, class Ref, class Ptr, class KOfT, class HF>friend struct __HTIterator;public:// 插入pair<iterator, bool> Insert(const T& data){HF hf;KOfT koft;iterator tmp = Find(koft(data));// 不允许有相同的值if (tmp != end())return make_pair(tmp, false);// 扩容if (_n == _table.size()){int newcapacity = 2 * _table.size();hash_table newtable(newcapacity);for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;int hashi = hf(koft(cur->_data)) % newcapacity;cur->_next = newtable._table[hashi];newtable._table[hashi] = cur;cur = next;}_table[i] = nullptr;}_table.swap(newtable._table);}// 头插 int hashi = hf(koft(data)) % _table.size();Node* newnode = new Node(data);newnode->_next = _table[hashi];_table[hashi] = newnode;_n++;return make_pair(iterator(newnode, this, hashi), true);}// 删除bool Erase(const K& key){Node* tmp = Find(key);// 找不到则删除失败if (tmp == nullptr)return false;HF hf;KOfT koft;int hashi = hf(key) % _table.size();Node* prev = nullptr;Node* cur = _table[hashi];while (cur){Node* next = cur->_next;if (koft(cur->_data) == key){if (prev == nullptr){_table[hashi] = next;}else{prev->_next = next;}_n--;delete cur;return true;}else{prev = cur;cur = next;}}return false;}private:vector<Node*> _table;int _n;};
}

1.2.9 哈希表的整体实现

#pragma once#include<iostream>
#include<string>
#include<vector>
#include<unordered_set>
#include<set>using namespace std;// 整数类型
template<class K>
struct HashFunc
{size_t operator()(const K& key){return (size_t)key;}
};// 特化
template<>
struct HashFunc<string>
{size_t operator()(const string& s){size_t sum = 0;for (size_t i = 0; i < s.size(); i++){sum = sum * 31 + s[i];}return sum;}
};namespace hash_bucket
{template<class T>struct HashNode{HashNode* _next;T _data;HashNode(const T& data):_data(data), _next(nullptr){}};// 前置声明,否则后面的__HTIterator找不到hash_tabletemplate<class K, class T, class KOfT, class HF>class hash_table;template<class K, class T , class Ref, class Ptr, class KOfT, class HF>struct __HTIterator{typedef HashNode<T> Node;typedef __HTIterator<K, T, Ref , Ptr , KOfT, HF> self;// 成员变量Node* _node;// 由于const迭代器中const修饰this// 所以这里需要加const,否则会导致权限放大// 编译无法通过的情况const hash_table<K, T, KOfT, HF>* _pht;// size_t hashi;// 构造函数__HTIterator(Node* node, hash_table<K, T, KOfT, HF>* pht , size_t i):_node(node),_pht(pht),hashi(i){}// 构造函数__HTIterator(Node* node, const hash_table<K, T, KOfT, HF>* pht, size_t i):_node(node), _pht(pht), hashi(i){}self& operator++(){if (_node->_next){// 若当前桶其他节点没有走完// 那么继续走下一个节点_node = _node->_next;}else{// 若当前桶的所有节点都已经走完// 那么继续向下一个桶不为空的桶走hashi++;while (hashi < _pht->_table.size()){if (_pht->_table[hashi]){_node = _pht->_table[hashi];break;}hashi++;}if (hashi == _pht->_table.size())_node = nullptr;}return *this;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const self& s){return _node != s._node;}};template<class K, class T ,  class KOfT , class HF>class hash_table{public:typedef HashNode<T> Node;typedef __HTIterator<K, T, T& , T* ,KOfT, HF> iterator;typedef __HTIterator<K, T, const T&, const T*, KOfT, HF> const_iterator;// 将__HTIterator设置为hash_table的友元// 使__HTIterator可以访问hash_table的私有template<class K, class T, class Ref, class Ptr, class KOfT, class HF>friend struct __HTIterator;public:// 构造函数hash_table(int n = 10):_table(vector<Node*>(n)),_n(0){}// 析构函数~hash_table(){Clear();}void Clear(){for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_table[i] = nullptr;}}size_t Size(){return _n;}void Swap(hash_table<K,T,KOfT,HF>& ht){_table.swap(ht._table);swap(_n, ht._n);}// 迭代器iterator begin(){for (size_t i = 0; i < _table.size(); i++){if (_table[i])return iterator(_table[i], this, i);}return end();}iterator end(){return iterator(nullptr, this, -1);}const_iterator begin()const{for (size_t i = 0; i < _table.size(); i++){if (_table[i])return const_iterator(_table[i], this, i);}return end();}const_iterator end()const{return const_iterator(nullptr, this, -1);}// 插入pair<iterator,bool> Insert(const T& data){HF hf;KOfT koft;iterator tmp = Find(koft(data));// 不允许有相同的值if (tmp != end())return make_pair(tmp,false);// 扩容if (_n == _table.size()){int newcapacity = 2 * _table.size();hash_table newtable(newcapacity);for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;int hashi = hf(koft(cur->_data)) % newcapacity;cur->_next = newtable._table[hashi];newtable._table[hashi] = cur;cur = next;}_table[i] = nullptr;}_table.swap(newtable._table);}// 头插 int hashi = hf(koft(data)) % _table.size();Node* newnode = new Node(data);newnode->_next = _table[hashi];_table[hashi] = newnode;_n++;return make_pair(iterator(newnode,this,hashi),true);}// 删除bool Erase(const K& key){Node* tmp = Find(key);// 找不到则删除失败if (tmp == nullptr)return false;HF hf;KOfT koft;int hashi = hf(key) % _table.size();Node* prev = nullptr;Node* cur = _table[hashi];while (cur){Node* next = cur->_next;if (koft(cur->_data) == key){if (prev == nullptr){_table[hashi] = next;}else{prev->_next = next;}_n--;delete cur;return true;}else{prev = cur;cur = next;}}return false;}// 查找iterator Find(const K& key){HF hf;KOfT koft;int hashi = hf(key) % _table.size();Node* cur = _table[hashi];while (cur){if (koft(cur->_data) == key)return iterator(cur,this,hashi);cur = cur->_next;}// 找不到返回空return end();}void Some(){size_t bucketSize = 0;size_t maxBucketLen = 0;size_t sum = 0;double averageBucketLen = 0;for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){++bucketSize;}size_t bucketLen = 0;while (cur){++bucketLen;cur = cur->_next;}sum += bucketLen;if (bucketLen > maxBucketLen){maxBucketLen = bucketLen;}}averageBucketLen = (double)sum / (double)bucketSize;printf("all bucketSize:%d\n", _table.size());printf("bucketSize:%d\n", bucketSize);printf("maxBucketLen:%d\n", maxBucketLen);printf("averageBucketLen:%lf\n\n", averageBucketLen);}private:vector<Node*> _table;int _n;public:void TestHT1(){hash_table<int, int> ht;int a[] = { 4,14,24,34,5,7,1 };for (auto e : a){ht.Insert(make_pair(e, e));}ht.Insert(make_pair(3, 3));ht.Insert(make_pair(3, 3));ht.Insert(make_pair(-3, -3));ht.Some();}void TestHT2(){string arr[] = { "香蕉", "甜瓜","苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };//HashTable<string, int, HashFuncString> ht;hash_table<string, int> ht;for (auto& e : arr){//auto ret = ht.Find(e);HashNode<string, int>* ret = ht.Find(e);if (ret){ret->_kv.second++;}else{ht.Insert(make_pair(e, 1));}}ht.Insert(make_pair("apple", 1));ht.Insert(make_pair("sort", 1));ht.Insert(make_pair("abc", 1));ht.Insert(make_pair("acb", 1));ht.Insert(make_pair("aad", 1));}void TestHT3(){const size_t N = 1000;unordered_set<int> us;set<int> s;hash_table<int, int> ht;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; ++i){//v.push_back(rand()); // N比较大时,重复值比较多v.push_back(rand() + i); // 重复值相对少//v.push_back(i); // 没有重复,有序}size_t begin1 = clock();for (auto e : v){s.insert(e);}size_t end1 = clock();cout << "set insert:" << end1 - begin1 << endl;size_t begin2 = clock();for (auto e : v){us.insert(e);}size_t end2 = clock();cout << "unordered_set insert:" << end2 - begin2 << endl;size_t begin3 = clock();for (auto e : v){ht.Insert(make_pair(e, e));}size_t end3 = clock();cout << "hash_table insert:" << end3 - begin3 << endl << endl;size_t begin4 = clock();for (auto e : v){s.find(e);}size_t end4 = clock();cout << "set find:" << end4 - begin4 << endl;size_t begin5 = clock();for (auto e : v){us.find(e);}size_t end5 = clock();cout << "unordered_set find:" << end5 - begin5 << endl;size_t begin6 = clock();for (auto e : v){ht.Find(e);}size_t end6 = clock();cout << "hash_table find:" << end6 - begin6 << endl << endl;cout << "插入数据个数:" << us.size() << endl << endl;ht.Some();size_t begin7 = clock();for (auto e : v){s.erase(e);}size_t end7 = clock();cout << "set erase:" << end7 - begin7 << endl;size_t begin8 = clock();for (auto e : v){us.erase(e);}size_t end8 = clock();cout << "unordered_set erase:" << end8 - begin8 << endl;size_t begin9 = clock();for (auto e : v){ht.Erase(e);}size_t end9 = clock();cout << "hash_table Erase:" << end9 - begin9 << endl << endl;}};}

二、unordered_set的实现及测试

#pragma once#include"HashTable.h"namespace aj
{template<class K>class unordered_set{public:struct SetKeyOfT{const K& operator()(const K& k){return k;}};public:// 因为unordered_set中K是不能被改变// 所以普通迭代器也无法改变K的值// 所以让普通迭代器也是const迭代器typedef typename hash_bucket::hash_table<const K, K, SetKeyOfT, HashFunc<K>>::const_iterator iterator;typedef typename hash_bucket::hash_table<const K, K, SetKeyOfT, HashFunc<K>>::const_iterator const_iterator;// 由于_ht.Insert(key)返回的普通迭代器// 而pair<iterator, bool>中的迭代器// 看起来是普通迭代器,但实际上是const迭代器// 所以这里不能直接 return _ht.Insert(key)pair<iterator, bool> insert(const K& key){auto tmp = _ht.Insert(key);return make_pair(iterator(tmp.first._node, tmp.first._pht, tmp.first.hashi), tmp.second);}// 由于这里普通迭代器也是const迭代器// 那么这里只保留const迭代器版本/*iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}*/const_iterator begin()const{return _ht.begin();}const_iterator end()const{return _ht.end();}bool erase(const K& key){return _ht.Erase(key);}iterator find(const K& key){return _ht.Find(key);}void clear(){_ht.Clear();}size_t size(){return _ht.Size();}void swap(unordered_set<K>& us){_ht.Swap(us._ht);}private:// unordered_set中K是不能被改变的hash_bucket::hash_table <const K, K , SetKeyOfT , HashFunc<K>> _ht;};void test_unordered_set(){// 17:05/*unordered_set<int> us;us.insert(5);us.insert(15);us.insert(52);us.insert(3);*///unordered_set<int>::iterator it = us.begin();//while (it != us.end())//{//	// *it += 5;//	cout << *it << " ";//	++it;//}//cout << endl;unordered_set<int> us1;us1.insert(1);us1.insert(2);us1.insert(3);us1.insert(4);unordered_set<int> us2;us2.insert(10);us2.insert(20);us2.insert(30);us2.insert(40);for (auto e : us1){cout << e << " ";}cout << endl;for (auto e : us2){cout << e << " ";}cout << endl;us1.swap(us2);for (auto e : us1){cout << e << " ";}cout << endl;for (auto e : us2){cout << e << " ";}cout << endl;}
}

三、unordered_map的实现及测试

#pragma once#include"HashTable.h"namespace aj
{template<class K , class V>class unordered_map{public:struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename hash_bucket::hash_table<K, pair<const K, V>, MapKeyOfT, HashFunc<K>>::iterator iterator;typedef typename hash_bucket::hash_table<K, pair<const K, V>, MapKeyOfT, HashFunc<K>>::const_iterator const_iterator;pair<iterator,bool> insert(const pair<K, V>& kv){return _ht.Insert(kv);}iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin()const{return _ht.begin();}const_iterator end()const{return _ht.end();}V& operator[](const K& key){auto tmp = _ht.Insert(make_pair(key,V()));return tmp.first->second;}const V& operator[](const K& key)const{auto tmp = _ht.Insert(make_pair(key, V()));return tmp.first->second;}bool erase(const K& key){return _ht.Erase(key);}iterator find(const K& key){return _ht.Find(key);}void clear(){_ht.Clear();}size_t size(){return _ht.Size();}void swap(unordered_map<K,V>& um){_ht.Swap(um._ht);}private:// unordered_map中K是不能被改变的hash_bucket::hash_table <K, pair<const K, V>, MapKeyOfT , HashFunc<K>> _ht;};void test_unordered_map1(){unordered_map<int, int> um;um.insert(make_pair(1, 1));um.insert(make_pair(2, 2));um.insert(make_pair(3, 3));um.insert(make_pair(4, 4));unordered_map<int, int>::iterator it = um.begin();while (it != um.end()){cout << it->first << ' ' << it->second << endl;++it;}}void test_unordered_map2(){unordered_map<string, string> dict;dict.insert(make_pair("sort", "排序"));dict.insert(make_pair("insert", "插入"));dict.insert(make_pair("string", "字符串"));for (auto& kv : dict){// kv.first += 'x';kv.second += 'x';cout << kv.first << ":" << kv.second << endl;}cout << endl;string arr[] = { "香蕉", "甜瓜","苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };unordered_map<string, int> count_map;for (auto& e : arr){count_map[e]++;}for (auto& kv : count_map){cout << kv.first << ":" << kv.second << endl;}cout << endl;}
}

结尾

到目前已经将哈希的大部分内容讲述完了,那么下一篇文章将继续完善哈希,讲述位图和布隆过滤器。

如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。
希望大家以后也能和我一起进步!!🌹🌹
如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹

在这里插入图片描述

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

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

相关文章

阐述 C 语言中的浮点数精度问题?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; &#x1f4d9;C 语言百万年薪修炼课程 【https://dwz.mosong.cc/cyyjc】通俗易懂&#xff0c;深入浅出&#xff0c;匠心打磨&#xff0c;死磕细节&#xff0c;6年迭代&…

matlab支持向量机使用错误

&#x1f3c6;本文收录于《CSDN问答解答》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&…

ROS的TF系统

一、SLAM 1、SLAM全称是Simultaneous Localization And Mapping&#xff0c;即同时定位与地图构建 2、SLAM软件包Hector_Mapping&#xff0c;←建图可参考链接所示文章 二、机器人定位 1、假设机器人开始建图的位置是地图坐标系的原点 2、则机器人在建图过程中的位置可以描…

DenseNet算法实现乳腺癌识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、前期准备 1.设置GPU import torch import torch.nn as nn import torchvision.transforms as transforms import torchvision from torchvision import …

CUDA原子操作

代码 #include <cuda_runtime.h> #include <stdio.h>__global__ void atomicAddAndGet(int *result, int *valueToAdd) {// 原子加法int addedValue atomicAdd(result, *valueToAdd);// 通过原子操作后读取值&#xff0c;确保是加法后的值addedValue *valueToAd…

黑马程序员2024最新SpringCloud微服务开发与实战 个人学习心得、踩坑、与bug记录 Day4

你好,我是Qiuner. 为帮助别人少走弯路和记录自己编程学习过程而写博客 这是我的 github https://github.com/Qiuner ⭐️ gitee https://gitee.com/Qiuner &#x1f339; 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我会…

HDFS Decommission节点的长尾分析和问题解决

文章目录 前言Decommission过慢的分析过程NameNode页面并不显示Decommission的进度和剩余块数量增加每次调度的块数量增加Stream Limit以避免节点被Skip节点被Skip时应该在DEBUG时打印原因在大量节点被Skip的时候加快有效调度其他可能改进 基本流程解析用户通过节点刷新触发Dec…

java Web学习笔记(一)

文章目录 1. 前置学习知识2. Tomcat介绍Tomcat目录文件介绍URL的组成部分和项目资源的对应关系idea配置部署tomcat并成功运行一个app-web项目 3. HTTP协议介绍&#xff08;很重要&#xff09;HTTP协议和HTTPS的区别HTTP协议的发展理解HTTP协议 报文格式报文响应的状态码 4. Ser…

以设备为核心的状态自动采集、人工运维和预测性维护为一体的智能管理系统

中服云设备全生命周期管理系统充分利用物联网、人工智能、机器学习、大数据等新一代技术&#xff0c;实现对企业生产设备从采购、安装、调试、使用、维护、维修、改造、更新直到报废全生命周期的智能化、数字化、可视化的实时管控&#xff0c;支持设备运行状态的自动采集和人工…

js 中 new Worker 报错 Failed to construct ‘Worker‘

new Worker("worker.js");运行多线程 Web Worker 的时候报错 Uncaught DOMException: Failed to construct ‘Worker’ 原因是浏览器不允许通过本地文件访问使用Web Worker。 解决方法&#xff1a; 将项目部署到服务器上或者用Node起本地服务访问项目

活动回顾|矩阵起源2024WAIC圆满落幕

2024年7月4日至7日&#xff0c;全球瞩目的2024世界人工智能大会暨人工智能全球治理高级别会议&#xff08;WAIC 2024&#xff09;在上海盛大举行&#xff0c;其中Future Tech 100未来之星创新孵化展再次成为大会焦点&#xff0c;它不仅是一场科技成果的展示&#xff0c;更是全球…

网络通信基本知识

网络通信 什么是网络通信&#xff1f; 通信网络是指将各个孤立的设备进行物理连接&#xff0c;实现人与人&#xff0c;人与计算机&#xff0c;计算机与计算机之间进行信息交换的链路&#xff0c;从而达到资源共享和通信的目的。 什么是网络协议&#xff1f; 网络协议是计算机…

SPring Boot整合第三方框架

springboot整合第三方框架 1. 整合mybatis 1.1引入依赖——pom.xml配置文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instanc…

安装isce2

今天再次尝试安装&#xff0c;之前试过2次都是卡在同一步&#xff0c;今天换成了用mamba conda就没有再报错了 全程参考云军老师的step by step教程&#xff0c;安装成功 GitHub - yunjunz/conda-envs: conda environment setup on Linux / macOS for InSAR data processing …

【进阶】利用python内置模块自动化发送邮件及邮件附件

目录 自动化发送邮件 流程&#xff1a; 步骤&#xff1a; 【重点】 【MIMEText--发送文本类型的邮件】 【MIMEImage-发送附件为图片的邮件】 【MIMEBase--发送附件为html报告的邮件】 自动化发送邮件 以qq邮箱为例&#xff0c;提前打开POP3/IMAP/SMTP/Exchange/CardDAV 服…

【Redis】复制(Replica)

文章目录 一、复制是什么&#xff1f;二、 基本命令三、 配置&#xff08;分为配置文件和命令配置&#xff09;3.1 配置文件3.2 命令配置3.3 嵌套连接3.4 关闭从属关系 四、 复制原理五、 缺点 以下是本篇文章正文内容 一、复制是什么&#xff1f; 主从复制 master&#xff…

GloVe: Global Vectors for Word Representation论文笔记解读

基本信息 作者Jeffrey Penningtondoi10.3115/v1/D14-1162发表时间2014期刊EMNLP网址https://aclanthology.org/D14-1162.pdf 研究背景 1. What’s known 既往研究已证实 全局矩阵分解方法&#xff1a;LSA&#xff0c;考虑整个语料库词频的统计信息得到共现矩阵&#xff0c;通…

根据视图矩阵, 恢复相机的世界空间的位置

根据视图矩阵, 恢复相机的世界空间的位置 一、方法1 glsl 实现: // 从本地局部坐标系(相机空间) 到 世界空间的旋转变换 mat3 getLocal2WorldRotation() {mat3 world2localRotation mat3(viewMatrix[0].xyz,viewMatrix[1].xyz,viewMatrix[2].xyz);return inverse(world2loca…

汽车零配件行业看板管理系统应用

生产制造已经走向了精益生产&#xff0c;计算时效产出、物料周转时间等问题&#xff0c;成为每一个制造企业要面临的问题&#xff0c;工厂更需要加快自动化&#xff0c;信息化&#xff0c;数字化的布局和应用。 之前的文章多次讲解了企业MES管理系统&#xff0c;本篇文章就为大…

论文er们,YOLO这口饭得趁热吃

不知道各位有没有看出来&#xff0c;从去年开始YOLO相关的论文就处于一个井喷式状态&#xff0c;SCI各区都能见到它的身影。 这是因为YOLO其实是个很好发论文的方向&#xff0c;需求量很大&#xff0c;热度高&#xff0c;并且好入门&#xff0c;能获取的资源也很多。写论文时一…