unordered_map与unordered_set的实现(含迭代器)

news2025/2/26 14:38:30

unordered_map与unordered_set的实现


文章目录

  • unordered_map与unordered_set的实现
  • 前言
  • 一、问题一
    • HashTable.h
  • 二、问题二&问题三
    • 1.封装时如何取出key
    • 2.不同类型key如何建立对应关系
  • 三、问题四&问题五
    • 问题四
    • 问题五
  • 四、实现代码
    • MyUnorderedSet.h
    • MyUnorderedMap.h
    • HashTable.h


前言

在C++11中新增了两个很好用的容器,分别是unordered_map与unordered_set,和map和set有不同之处

map与set的底层实现是平衡树——红黑树,并且采取中序遍历时默认有序

而本文的unordered_map与unordered_set底层实现是哈希思想(拉链法),并且他的存储方式不支持排序,所以是unordered

两者的查找效率综合比较,会发现unordered_map与unordered_set的效率综合会更高,所以在C++11中支持了unordered_map与unordered_set

本文中是用哈希桶的拉链法来封装unordered_map与unordered_set
这里的封装与红黑树封装map和set的封装相似,但此处会更难

具体通过解决问题来实现

  1. HashTable的迭代器实现
  2. 封装时HashTable如何取出map的key和set的key
  3. 取出key后如何针对不同类型key建立映射关系
  4. 如何解决Set中key不能被修改,Map中key不能被修改,value能被修改的问题
  5. Insert插入返回值问题以及Map[]的重载实现

关于哈希思想及其具体实现细节看我的上篇文章:数据结构之哈希表


一、问题一

这里就一步步,先实现iterator再实现const_iterator版本了,而是直接放出能适配iterator与const_iterator的版本,本质上就是用类模板泛型编程,需要什么就调用什么,是什么类型就返回什么类型的迭代器

这里尤为注意的一点是,这里的迭代器是单向迭代器,只支持++,而由于底层是哈希表的拉链法实现的,是数组与链表结合的方式
在实现运算符重载++时,本质上就是在逐个遍历哈希桶,而当前桶走完的时候,需要进入下一个桶,那么如何判断当前桶的位置,以及如何找到下一个桶,就需要把这个数组或者整个哈希表传过来,这里我们做的是把整个哈希表传过来

注意:其实将数组传过来会更简单些,传哈希表会有一些问题

  1. 我们将哈希表传过来,是可能要访问哈希表内的私有变量来获得下一个桶,而直接在_HTIterator这个类内使用哈希表内的私有变量是不可取的,所以需要在哈希表内声明友元
  2. 此处还涉及编译问题,由于编译器是从上往下编译代码,我们将迭代器写在哈希表代码的上面,而迭代器中有哈希表,这里编译器并不认识哈希表,因为哈希表的定义还未出现,所以还需要哈希表对应的类的声明
	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
	friend struct _HTIterator;
	template<class K, class T, class KeyOfT, class Hash>
	class HashTable;

至于class KeyOfT 与 class Hash这两个类模板的作用,则在下文中解答

HashTable.h

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

		T _data;
		HashNode* _next;
	};


	template<class K, class T, class KeyOfT, class Hash>
	class HashTable;

	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
	struct _HTIterator
	{
		typedef HashNode<T> Node;
		typedef _HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;

		typedef _HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;


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

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

		_HTIterator(const iterator& x)
			:_node(x._node)
			, _pht(x._pht)
		{}


		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}



		Self operator++()
		{
			Hash hf;
			KeyOfT kot;

			size_t hashi = hf(kot(_node->_data)) % _pht->_t.size();

			if (_node->_next != nullptr)
			{
				_node = _node->_next;
			}
			else
			{
				hashi++;

				while (hashi < _pht->_t.size())
				{
					if (_pht->_t[hashi])
					{
						_node = _pht->_t[hashi];
						break;
					}

					hashi++;
				}

			}
			
			if (hashi == _pht->_t.size())
			{
				_node = nullptr;
			}

			return *this;
		}
		
		bool operator==(const Self& it)
		{
			return _node == it._node;
		}

		bool operator!=(const Self& it)
		{
			return _node != it._node;
		}

		const HashTable<K, T, KeyOfT, Hash>* _pht;
		Node* _node;
	};



	template<class K, class T, class KeyOfT, class Hash>
	class HashTable
	{
		typedef HashNode<T> Node;

		template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
		friend struct _HTIterator;

	public:

		HashTable(size_t n = 10)
		{
			_t.resize(n);
		}


		typedef _HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;
		typedef _HTIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;


		iterator begin()
		{
			size_t hashi = 0;

			while (hashi < _t.size())
			{
				if (_t[hashi])
				{
					break;
				}

				hashi++;
			}

			if (hashi == _t.size())
			{
				return iterator(nullptr, this);
			}

			return iterator(_t[hashi], this);
		}

		iterator end()
		{
			return iterator(nullptr, this);
		}



		const_iterator begin()const
		{
			size_t hashi = 0;

			while (hashi < _t.size())
			{
				if (_t[hashi])
				{
					break;
				}

				hashi++;
			}

			if (hashi == _t.size())
			{
				return iterator(nullptr, this);
			}

			return iterator(_t[hashi], this);
		}

		const_iterator end()const
		{
			return iterator(nullptr, this);
		}



		pair<iterator,bool> Insert(const T& data)
		{
			KeyOfT kot;
			Hash hf;

			iterator ret = Find(kot(data));
			if (ret != end())
			{
				return make_pair(ret, false);
			}
	
			//扩容
			if (_n / _t.size() == 1)
			{
				size_t newsize = _t.size() * 2;

				HashTable newtable(newsize);

				for (int i = 0; i < _n; i++)
				{
					Node* cur = _t[i];
					while (cur)
					{
						Node* next = cur->_next;

						size_t hashi = hf(kot(cur->_data)) % newsize;

						cur->_next = newtable._t[hashi];
						newtable._t[hashi] = cur;

						cur = next;
					}

					_t[i] = nullptr;
				}

				swap(_t, newtable._t);
			}


			size_t  hashi = hf(kot(data)) % _t.size();

			Node* newnode = new Node(data);

			newnode->_next = _t[hashi];
			_t[hashi] = newnode;

			_n++;

			return make_pair(iterator(newnode, this), true);
		}

		iterator Find(const K& key)
		{
			Hash hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _t.size();

			Node* cur = _t[hashi];

			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur, this);
				}
				
				cur = cur->_next;
			}

			return iterator(nullptr, this);
		}

		bool Erase(const K& key)
		{
			Hash hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _t.size();

			Node* cur = _t[hashi];
			Node* prev = nullptr;

			if (kot(cur->_data) == key)
			{
				_t[hashi] = cur->_next;
				delete cur;
				return true;
			}

			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					prev->_next = cur->_next;
					delete cur;
					_n--;
					return true;
				}
				prev = cur;
				cur = cur->_next;
			}
			return false;
		}


	private:
		vector<HashNode<T>*> _t;
		size_t _n = 0;
	};
}

二、问题二&问题三

1.封装时如何取出key

首先解释一下问题,我们的目的是将unordered_map与unordered_set用哈希表封装实现,map中存的是pair,set中存的是key,而如何用一份哈希表适配两种结构呢

在封装的时候解决这个问题,在unordered_map与unordered_set中写一个内部类,这个类之中实现了一个仿函数,用来返回key,并且将其传给哈希表内

	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
	public:

		struct KeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return  kv.first;
			}
		};
	private:
		hash_bucket::HashTable<K, pair<const K, V>, KeyOfT, Hash> _ht;
	};

template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
	public:
		struct KeyOfT
		{
			const K& operator()(const K& key)
			{
				return  key;
			}
		};

	private:
		hash_bucket::HashTable<K, K, KeyOfT, Hash> _ht;
	};

2.不同类型key如何建立对应关系

本文的拉链法中,使用的哈希函数是除留余数法,如果key是int类型的话那正好,可以直接用key除,但如果key是string或者自定义类型,那么就不能够直接除了,则需要将其转换成int类型,另外一个模板参数Hash,则是将其转换方式传给哈希表

如果是内置类型的float,double之类的,我们可以直接强转成size_t返回
如果是string类型,由于string比较常用,我们可以为string搞个特化,默认支持string

上面两个都是默认支持的,用默认缺省值就行,不需要手动传Hash

而如果是自定义类型,则需要使用者通过接口手动传Hash,因为默认的缺省值用不了

template<class K>
struct HashFunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

template<>
struct HashFunc<string>
{
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (auto e : key)
		{
			hash *= 13;
			hash += e;
		}

		return hash;
	}
};

三、问题四&问题五

问题四与问题五与set与map用红黑树封装的问题相同

问题四

set的iterator和const_iterator都是红黑树的const_iterator复用而来
map中的iterator是红黑树的iterator复用而来,const_iterator是红黑树的const_iterator复用而来
既然set中的迭代器都是const_iterator所以key自然不能被修改

	typedef typename hash_bucket::HashTable<K, K, KeyOfT, Hash>::const_iterator iterator;
	typedef typename hash_bucket::HashTable<K, K, KeyOfT, Hash>::const_iterator const_iterator;

map解决key不能被修改,value能被修改的原理也很简单,就是在实例化的时候,声明第二个模板参数——在map中也就是pair,pair的first是const类型

	private:
		hash_bucket::HashTable<K, pair<const K, V>, KeyOfT, Hash> _ht;

问题五

unordered_map与unordered_set与map和set的红黑树封装相同,insert的返回值都是一个键值对
在这里插入图片描述

first是一个迭代器,second是一个bool类型

基于此性质,引出了map的计数功能,可以通过insert返回的迭代器查看是否有key值,如果不存在则插入,将value值赋值为1,如果key已经存在,则通过insert返回的迭代器将value++,以此实现计数功能,所以map实现了operator[],用来计数

	V& operator[](const K& key)
	{
		return _ht.Insert(make_pair(key, V())).first->second;
	}

四、实现代码

MyUnorderedSet.h

#include "HashTable.h"

namespace Tlzns
{
	template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
	public:
		struct KeyOfT
		{
			const K& operator()(const K& key)
			{
				return  key;
			}
		};



		typedef typename hash_bucket::HashTable<K, K, KeyOfT, Hash>::const_iterator iterator;
		typedef typename hash_bucket::HashTable<K, K, KeyOfT, Hash>::const_iterator const_iterator;



		iterator begin()const
		{
			return _ht.begin();
		}

		iterator end()const
		{
			return _ht.end();
		}


		pair<iterator, bool> Insert(const K& key)
		{
			auto ret = _ht.Insert(key);
			return pair<iterator, bool>(iterator(ret.first._node, ret.first._pht), ret.second);
		}

		bool Erase(const K& key)
		{
			return _ht.Erase(key);
		}

		iterator Find(const K& key)
		{
			return _ht.Find(key);
		}

	private:
		hash_bucket::HashTable<K, K, KeyOfT, Hash> _ht;
	};


	void test_set()
	{
		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;

		for (auto e : us)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}

MyUnorderedMap.h

#include "HashTable.h"

namespace Tlzns
{
	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
	public:

		struct KeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return  kv.first;
			}
		};



		typedef typename hash_bucket::HashTable<K, pair<const K, V>, KeyOfT, Hash>::iterator iterator;
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, KeyOfT, Hash>::const_iterator 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();
		}


		pair<iterator, bool> Insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}
		
		bool Erase(const K& key)
		{
			return _ht.Erase(key);
		}

		iterator Find(const K& key)
		{
			return _ht.Find(key);
		}

		V& operator[](const K& key)
		{
			return _ht.Insert(make_pair(key, V())).first->second;
		}


	private:
		hash_bucket::HashTable<K, pair<const K, V>, KeyOfT, Hash> _ht;
	};

	void test_map()
	{
		unordered_map<string, string> dict;
		dict.Insert(make_pair("sort", ""));
		dict.Insert(make_pair("string", ""));
		dict.Insert(make_pair("insert", ""));

		unordered_map<string, string>::const_iterator it = dict.begin();

		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;
	}
}

HashTable.h

#include <iostream>
#include <vector>

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& key)
	{
		size_t hash = 0;
		for (auto e : key)
		{
			hash *= 13;
			hash += e;
		}

		return hash;
	}
};

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

		T _data;
		HashNode* _next;
	};


	template<class K, class T, class KeyOfT, class Hash>
	class HashTable;

	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
	struct _HTIterator
	{
		typedef HashNode<T> Node;
		typedef _HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;

		typedef _HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;


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

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

		_HTIterator(const iterator& x)
			:_node(x._node)
			, _pht(x._pht)
		{}


		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}



		Self operator++()
		{
			Hash hf;
			KeyOfT kot;

			size_t hashi = hf(kot(_node->_data)) % _pht->_t.size();

			if (_node->_next != nullptr)
			{
				_node = _node->_next;
			}
			else
			{
				hashi++;

				while (hashi < _pht->_t.size())
				{
					if (_pht->_t[hashi])
					{
						_node = _pht->_t[hashi];
						break;
					}

					hashi++;
				}

			}
			
			if (hashi == _pht->_t.size())
			{
				_node = nullptr;
			}

			return *this;
		}
		
		bool operator==(const Self& it)
		{
			return _node == it._node;
		}

		bool operator!=(const Self& it)
		{
			return _node != it._node;
		}

		const HashTable<K, T, KeyOfT, Hash>* _pht;
		Node* _node;
	};



	template<class K, class T, class KeyOfT, class Hash>
	class HashTable
	{
		typedef HashNode<T> Node;

		template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
		friend struct _HTIterator;

	public:

		HashTable(size_t n = 10)
		{
			_t.resize(n);
		}


		typedef _HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;
		typedef _HTIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;


		iterator begin()
		{
			size_t hashi = 0;

			while (hashi < _t.size())
			{
				if (_t[hashi])
				{
					break;
				}

				hashi++;
			}

			if (hashi == _t.size())
			{
				return iterator(nullptr, this);
			}

			return iterator(_t[hashi], this);
		}

		iterator end()
		{
			return iterator(nullptr, this);
		}



		const_iterator begin()const
		{
			size_t hashi = 0;

			while (hashi < _t.size())
			{
				if (_t[hashi])
				{
					break;
				}

				hashi++;
			}

			if (hashi == _t.size())
			{
				return iterator(nullptr, this);
			}

			return iterator(_t[hashi], this);
		}

		const_iterator end()const
		{
			return iterator(nullptr, this);
		}



		pair<iterator,bool> Insert(const T& data)
		{
			KeyOfT kot;
			Hash hf;

			iterator ret = Find(kot(data));
			if (ret != end())
			{
				return make_pair(ret, false);
			}
	
			//扩容
			if (_n / _t.size() == 1)
			{
				size_t newsize = _t.size() * 2;

				HashTable newtable(newsize);

				for (int i = 0; i < _n; i++)
				{
					Node* cur = _t[i];
					while (cur)
					{
						Node* next = cur->_next;

						size_t hashi = hf(kot(cur->_data)) % newsize;

						cur->_next = newtable._t[hashi];
						newtable._t[hashi] = cur;

						cur = next;
					}

					_t[i] = nullptr;
				}

				swap(_t, newtable._t);
			}


			size_t  hashi = hf(kot(data)) % _t.size();

			Node* newnode = new Node(data);

			newnode->_next = _t[hashi];
			_t[hashi] = newnode;

			_n++;

			return make_pair(iterator(newnode, this), true);
		}

		iterator Find(const K& key)
		{
			Hash hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _t.size();

			Node* cur = _t[hashi];

			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur, this);
				}
				
				cur = cur->_next;
			}

			return iterator(nullptr, this);
		}

		bool Erase(const K& key)
		{
			Hash hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _t.size();

			Node* cur = _t[hashi];
			Node* prev = nullptr;

			if (kot(cur->_data) == key)
			{
				_t[hashi] = cur->_next;
				delete cur;
				return true;
			}

			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					prev->_next = cur->_next;
					delete cur;
					_n--;
					return true;
				}
				prev = cur;
				cur = cur->_next;
			}
			return false;
		}

	private:
		vector<HashNode<T>*> _t;
		size_t _n = 0;
	};
}

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

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

相关文章

C 语言实现TCP 通信,以及地址复用

服务端 #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <arpa/inet.h>int main() {//监听套接字文件描述符int listenFd -1;//连接套接字的文件描述符int connFd -1;//服务器的地址结构st…

python scipy.spatial.distance.pdist学习详记——(待完善)

1.Python scipy.spatial.distance.pdist用法及代码示例

《许犁庭与柔性世界》第二十九章 掷红鸟

“没事&#xff0c;其实我也想到了你会走&#xff0c;谁能在那又臭又脏的烟囱里待那么久。所以我就急匆匆地出去找你&#xff0c;找遍了附近都找不着&#xff0c;却突然在大屏幕上看到你正受着陆捋须的折磨。” “唔~~~” “这死家伙可真过分&#xff01;难怪学院里那么多人讨厌…

继续画图带你学习TCP 其他 7 大特性

四、滑动窗口机制 五、流量控制 六、拥塞控制 (安全机制) 七、延迟应答 (效率机制) 八、捎带应答 (效率机制) 九、粘包问题 十、保活机制 TCP总结 四、滑动窗口机制 滑动窗口机制&#xff0c;是在可靠性的前提下&#xff0c;进一步地提高传输效率 认识滑动窗口 一发一收…

24.Python 网络编程之socket编程

目录 1.认识TCP/IP2.socket编程2.1 使用socket2.2 使用socketserver 1.认识TCP/IP 计算机网络就是把各个计算机连接在一起&#xff0c;在网络中的计算机可以互相通信。 网络编程是如何在程序中实现两台计算机的通信。 网络通信是两台计算机上的两个进程之间的通信。 为了把…

第十一节HarmonyOS 常用容器组件3-Tabs

一、Tabs 1、概述 我们经常使用时&#xff0c;会出现试图切换的场景&#xff0c;比如底部有多个菜单&#xff0c;“首页”、 “我的”等。 两个内容容器的切换&#xff1a; ArkUI开发框架提供了一种页签容器组件Tabs&#xff0c;开发者通过Tabs组件可以很容易的实现内容视图…

目标检测算法改进系列之添加变核卷积AKConv模块

AKConv变核卷积 KConv的主要思想&#xff1a;AKConv&#xff08;可变核卷积&#xff09;主要提供一种灵活的卷积机制&#xff0c;允许卷积核具有任意数量的参数和采样形状。这种方法突破了传统卷积局限于固定局部窗口和固定采样形状的限制&#xff0c;从而使得卷积操作能够更加…

C# OpenVINO 直接读取百度Paddle模型实现物体检测( yolov3_darknet)

目录 效果 项目 代码 下载 C# OpenVINO 直接读取百度Paddle模型实现物体检测( yolov3_darknet) 效果 项目 代码 using OpenCvSharp; using Sdcb.OpenVINO; using Sdcb.OpenVINO.Natives; using System; using System.Collections.Generic; using System.Diagnostics; usi…

Micropython for QNX编译过程

Micropython for QNX编译过程 执行步骤 1. https://github.com/micropython/micropython select tag 1.20.0 git clone micropython 2. make -C mpy-cross 3. 修改py/mkenv.mk CROSS_COMPILE ntoaarch64- 注意如果这步必须在make -C mpy-cross 之后执行&#xff0c;如果需要重…

什么?居然可以免费使用Jetbrains?!

JetBrains是一家捷克的软件开发公司&#xff0c;该公司位于捷克的布拉格&#xff0c;并在俄罗斯的圣彼得堡及美国麻州波士顿都设有办公室&#xff0c;该公司最为人所熟知的产品是Java编程语言开发撰写时所用的集成开发环境&#xff1a;IntelliJ IDEA。 如下是jetbrains旗下的产…

Ps:使用 Emoji 字符

Emoji 字符是一种在数字通讯中广泛使用的小图像或表情符号&#xff0c;用于表达情感、活动、物体、地点、天气情况等。 Emoji 源自日本&#xff0c;但现已成为全球数字沟通的一部分。这些字符通常是彩色的&#xff0c;并且能够在不同的设备和平台上保持一致性。 通常&#xff0…

以用户为中心的前端性能

1. 简介 前端性能跟用户体验息息相关。举个栗子&#xff0c;当你打开乘车码扫码进站&#xff0c;网页白屏了很久才加载出来&#xff0c;延误了乘车时间&#xff1b;当你在微信抢红包时&#xff0c;点击按钮后延迟了一会才开始转圈圈&#xff0c;最终没抢到红包。当出现这样的情…

python自学之《艾伯特用Python做科学计算》(1)——(待完善)

好吧&#xff0c;刚开始就打了一波而广告 啄木鸟社区的Python图书概览&#xff1a; http://wiki.woodpecker.org.cn/moin/PyBooks &#xff08;22/388&#xff09;

pytorch 模型量化quantization

pytorch 模型量化quantization 1.workflow1.1 PTQ1.2 QAT 2. demo2.1 构建resnet101_quantization模型2.2 PTQ2.3 QAT 参考文献 pytorch框架提供了三种量化方法&#xff0c;包括&#xff1a; Dynamic QuantizationPost-Training Static Quantization&#xff08;PTQ&#xff0…

基于Logistic回归实现二分类

目录 Logistic回归公式推导&#xff1a; Sigmoid函数&#xff1a; Logistic回归如何实现分类&#xff1a; 优化的方法&#xff1a; 代码&#xff1a; 1.创建一个随机数据集&#xff0c;分类直线为y2x&#xff1a; 为什么用np.hstack()增加一列1&#xff1f; 为什么返回…

协同过滤算法:个性化推荐的艺术与科学

目录 引言&#xff1a; 一、协同过滤算法的基本原理 二、协同过滤算法的应用领域 三、协同过滤算法的优缺点 四、协同过滤算法的未来发展方向 五、结论 引言&#xff1a; 在当今数字化时代&#xff0c;信息过载成为了一个普遍的问题。为了帮助人们更好地发现符合个性化需…

Linux驱动开发学习笔记2《LED驱动开发试验》

目录 一、Linux下LED灯驱动原理 1.地址映射 二、硬件原理图分析 三、实验程序编写 1.LED 灯驱动程序编写 2.编写测试APP 四、运行测试 1.编译驱动程序和测试APP &#xff08;1&#xff09;编译驱动程序 &#xff08;2&#xff09;编译测试APP 2.运行测试 一、Linux下…

分享81个节日PPT,总有一款适合您

分享81个节日PPT&#xff0c;总有一款适合您 81个节日PPT下载链接&#xff1a;https://pan.baidu.com/s/1V0feg5pZ8C1Szycy40CrUw?pwd6666 提取码&#xff1a;6666 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不易…

Android CardView基础使用

目录 一、CardView 1.1 导入material库 1.2 属性 二、使用(效果) 2.1 圆角卡片效果 2.2 阴影卡片效果 2.3 背景 2.3.1 设置卡片背景(app:cardBackgroundColor) 2.3.2 内嵌布局&#xff0c;给布局设置背景色 2.4 进阶版 2.4.1 带透明度 2.4.2 无透明度 一、CardView 顾名…

【编码魔法师系列_构建型1.3 】抽象工厂模式(Abstract Factory)

学会设计模式&#xff0c;你就可以像拥有魔法一样&#xff0c;在开发过程中解决一些复杂的问题。设计模式是由经验丰富的开发者们&#xff08;GoF&#xff09;凝聚出来的最佳实践&#xff0c;可以提高代码的可读性、可维护性和可重用性&#xff0c;从而让我们的开发效率更高。通…