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

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.rhkb.cn/news/209290.html

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

相关文章

1949-2021年全国31省公路里程数据

1949-2021年全国31省公路里程数据 1、指标&#xff1a;公路里程 2、范围&#xff1a;包括31省 1978-2021年期间无缺失 3、来源&#xff1a;各省NJ、产业NJ、各省统计GB 4、指标解释&#xff1a;公路里程指报告期末公路的实际长度。 统计范围&#xff1a;包括城间、城乡间、乡…

【C语言】字符串函数strlen #strcpy #strcmp #strcat #strstr及其模拟实现

在C语言中&#xff0c;有一种特殊的数据类型&#xff0c;即字符串类型。C 并没有专门定义一个字符串类型&#xff0c;这对我们使用字符串造成了一定的麻烦。但是&#xff0c;C标准库<string.h> 中定义了各种字符串函数&#xff0c;这对于我们来说是一件值得庆幸的事情。…

node.js-连接SQLserver数据库

1.在自己的项目JS文件夹中建文件&#xff1a;config.js、mssql.js和server.js以及api文件夹下的user.js 2.在config.js中封装数据库信息 let app {user: sa, //这里写你的数据库的用户名password: ,//这里写数据库的密码server: localhost,database: medicineSystem, // 数据…

优秀编程习惯一: Git提交如何写注释

feat feat - A new feature : 一个新功能 fix fix - A bug fix : bug修复 docs docs - Documentation only changes : 仅更改文档 style style - Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) : 不影响代…

vue3 + mark.js | 实现文字标注功能

页面效果 具体实现 新增 1、监听鼠标抬起事件&#xff0c;通过window.getSelection()方法获取鼠标用户选择的文本范围或光标的当前位置。2、通过 选中的文字长度是否大于0或window.getSelection().isCollapsed (返回一个布尔值用于描述选区的起始点和终止点是否位于一个位置&…

【FPGA】Verilog:二进制并行加法器 | 超前进位 | 实现 4 位二进制并行加法器和减法器 | MSI/LSI 运算电路

Ⅰ. 前置知识 0x00 并行加法器和减法器 如果我们要对 4 位加法器和减法器进行关于二进制并行运算功能&#xff0c;可以通过将加法器和减法器以 N 个并行连接的方式&#xff0c;创建一个执行 N 位加法和减法运算的电路。 4 位二进制并行加法器 4 位二进制并行减法器 换…

vue学习笔记(八)——Vue组件-进阶(插槽、自定义指令)

一、Vue组件进阶 1.1 动态组件 多个组件使用同一个挂载点&#xff0c;并动态切换 效果如下: 1. 准备被切换的 - UserName.vue / UserInfo.vue 2个组件 2. 引入到UseDynamic.vue注册 3. 准备变量来承载要显示的"组件名" 4. 设置挂载点<component>&#xf…

Elasticsearch:对时间序列数据流进行降采样(downsampling)

降采样提供了一种通过以降低的粒度存储时间序列数据来减少时间序列数据占用的方法。 指标&#xff08;metrics&#xff09;解决方案收集大量随时间增长的时间序列数据。 随着数据老化&#xff0c;它与系统当前状态的相关性越来越小。 降采样过程将固定时间间隔内的文档汇总为单…

VC++调试QT源码

环境&#xff1a;vs2017 qt 5.14.2 1&#xff1a;首先我们需要选择我们的源码路径 右键解决方案-》属性-》通用属性-》调试源文件-》在窗口内添加QT下载时的源码**.src文件夹**&#xff0c;这里最好把源码 D:\software\QT\path\5.14.2\Src 源文件里面的Src文件做一个备份出来…

<蓝桥杯软件赛>零基础备赛20周--第8周第2讲--排序的应用

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周&#xff08;读者可以按…

MFC 绘制单一颜色圆形、渐变颜色边框圆形、渐变填充圆形以及绘制三角函数正弦函数曲线.

MFC 绘制三种不同圆形以及绘制正弦函数曲线 本文使用visual Studio MFC 平台实现绘制单一颜色圆形、渐变颜色边框圆形、渐变填充圆形以及绘制三角函数正弦函数曲线. 关于基础工程的创建请参考 01-Visual Studio 使用MFC 单文档工程绘制单一颜色直线和绘制渐变颜色的直线 02-vis…

百度收录批量查询工具,免费SEO优化排名工具

拥有一个在搜索引擎中得到良好收录的网站对于个人和企业都至关重要。而百度&#xff0c;作为中国最大的搜索引擎&#xff0c;其收录情况直接影响着网站的曝光度和流量。 百度搜索引擎是中文用户获取信息的重要途径之一。而在这个竞争激烈的网络环境中&#xff0c;了解自己网站…

重启路由器可以解决N多问题?

为什么重启始终是路由器问题的首要解决方案? 在日常的工作学习工作中,不起眼的路由器是一种相对简单的设备,但这仍然是我们谈论的计算机。 这种廉价的塑料外壳装有 CPU、随机存取存储器 (RAM)、只读存储器 (ROM) 和许多其他组件。 该硬件运行预装的软件(或固件)来管理连接…

vue之mixin混入

vue之mixin混入 mixin是什么&#xff1f; 官方的解释&#xff1a; 混入 (mixin) 提供了一种非常灵活的方式&#xff0c;来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时&#xff0c;所有混入对象的选项将被“混合”进入该组件本身的…

严蔚敏数据结构题集 p18(2.25——2.30)(c语言代码实现)

目录 2.25假设以两个元素依值递增有序排列的线性表A和B分别表示两个集合(即同一表中的元素值各不相同),现要求另辟空间构成一个线性表C,其元素为A和B中元素的交集,且表C中的元素也依值递增有序排列。试对顺序表编写求C的算法。 2.26要求同2.25题。是对单链表编写求C的算法 2.…

[架构之路-256]:目标系统 - 设计方法 - 软件工程 - 软件设计 - 架构设计 - 软件系统不同层次的复用与软件系统向越来越复杂的方向聚合

目录 前言&#xff1a; 一、CPU寄存器级的复用&#xff1a;CPU寄存器 二、指令级复用&#xff1a;二进制指令 三、过程级复用&#xff1a;汇编语言 四、函数级复用&#xff1a;C语言 五、对象级复用&#xff1a;C, Java, Python 六、组件级复用 七、服务级复用 八、微…

Swift下如何使用#if条件编译

一、OC使用条件编译 OC中可以使用宏定义&#xff0c;再使用条件编译 #define USER_CUSTOM使用 #if USER_CUSTOM //其他代码 #endif二、Swift使用条件编译 Swift 不像ObjectC一样&#xff0c;通过定义一个变量&#xff0c;然后使用**#if #endif** 方法。swift需要设置一下才能…

2 文本分类入门:TextCNN

论文链接&#xff1a;https://arxiv.org/pdf/1408.5882.pdf TextCNN 是一种用于文本分类的卷积神经网络模型。它在卷积神经网络的基础上进行了一些修改&#xff0c;以适应文本数据的特点。 TextCNN 的主要思想是使用一维卷积层来提取文本中的局部特征&#xff0c;并通过池化操…

搭建Appium工具环境

1、安装Java Development Kit&#xff08;JDK&#xff09; 前往Oracle官网下载JDK。 在https://www.oracle.com/java/technologies/javase-jdk11-downloads.html 找到最新版本的JDK。根据操作系统选择适合的版本&#xff0c;并根据指示下载安装程序。 安装JDK。运行下载的安…

Unittest单元测试之unittest用例执行顺序

unittest用例执行顺序 当在一个测试类或多个测试模块下&#xff0c;用例数量较多时&#xff0c;unittest在执行用例 &#xff08;test_xxx&#xff09;时&#xff0c;并不是按从上到下的顺序执行&#xff0c;有特定的顺序。 unittest框架默认根据ACSII码的顺序加载测试用例&a…