unordered_map和unordered_set

目录

一、unordered_map

1.1、unordered_map的特点

1.2、unordered_map和map的区别

二、unordered_set

2.1、unordered_set的特点

2.2、unordered_set和set的区别

三、哈系桶的改造

3.1 结构设置

3.2 构造函数和析构函数

3.3 数据插入

3.4 数据查找

3.5 数据删除

3.6 迭代器实现

3.7、友元声明

​编辑

四、unordered_map封装

五、unordered_set封装

六、哈希表


一、unordered_map

unordered_map其实就是与map相对应的一个容器。学过map就知道,map的底层是一个红黑树,通过中序遍历的方式可以以有序的方式遍历整棵树。而unordered_map,正如它的名字一样,它的数据存储其实是无序的,这也和它底层所使用的哈希结构有关。而在其他功能上,unordered_map和map基本上就是一致的。

1.1、unordered_map的特点

(1)unordered_map是用于存储<key, value>键值对的关联式容器,它允许通过key快速的索引到对应的value。

(2)在内部,unorder_map没有对<key, value>按照任何特定的顺序排序,为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的<key, value>键值对放在相同的桶中。

(3)unordered_map容器的搜索效率比map快,但它在遍历元素自己的范围迭代方面效率就比较低。

(4)它的迭代器只能向前迭代,不支持反向迭代。

1.2、unordered_map和map的区别

从大结构上看,unordered_map和map的模板其实没有太大差距。学习了map和set我们就应该知道,map是通过T来告诉红黑树要构造的树的存储数据类型的,unordered_map也是一样的,但是它的参数中多了Hash和Pred两个参数,这两个参数都传了仿函数,主要和哈希结构有关。这里先不过多讲解

二、unordered_set

unordered_set其实也是一样的,从功能上来看和set并没有什么区别,只是由于地层数据结构的不同,导致unordered_set的数据是无序的,但是查找效率非常高。

2.1、unordered_set的特点

  1. 无序性:unordered_set中的元素没有特定的顺序,不会根据插入的顺序或者元素的值进行排序。

  2. 唯一性:unordered_set中的元素是唯一的,不允许重复的元素。

  3. 快速查找:unordered_set使用哈希表实现,可以在平均常数时间内进行查找操作,即使在大型数据集上也能保持高效。

  4. 插入和删除效率高:unordered_set的插入和删除操作的平均时间复杂度为常数时间,即O(1)。

  5. 高效的空间利用:unordered_set使用哈希表来存储元素,不会浪费额外的空间。

  6. 不支持修改操作:由于unordered_set中的元素是唯一的,不支持直接修改元素的值,需要先删除旧值,再插入新值。

  7. 迭代器失效:在进行插入和删除

2.2、unordered_set和set的区别

很明显,unordered_set相较于set,多了Hash和Pred两个参数。这两个参数都是传了仿函数,和unordered_map与map之间的关系都是一样的,这里先不过多讲解。

三、哈系桶的改造

3.1 结构设置

首先,这里实现的哈希桶需要能够同时支持unordered_map和unordered_set的调用。因此,在传入的数据中就需要有一个T,来标识传入的数据类型。同时,还需要有Hash函数和KeyOfT来分别对传入的数据转换为整形和获取传入数据的key值,主要是提供给使用了KV模型的数据。

我们还要知道,哈希桶其实是保存在一个顺序表中的,每个下标对应的位置上都是桶的头节点,每个桶中的数据以单链表的方式链接起来。因此,我们就需要一个vector来存储结构体指针,这个结构体中包含了当前节点存储的数据和下一个节点的位置。当然,还有有一个_n来记录顺序表中数据的个数。

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 HashBucket{template<class K, class T, class Hash, class KeyOfT>friend struct HashIterator;//友元声明,让迭代器可以访问哈希桶的私有成员typedef HashNode<T> Node;public:typedef HashIterator<K, T, Hash, KeyOfT> iterator;private:vector<Node*> _bucket;//存储数据的指针数组size_t _n;};
}

在类的模板参数中,Hash为将数据转化为整形的仿函数KeyOfT是返回键值的仿函数。

注意,上面的代码中有一个迭代器的重命名和迭代器结构体的友元声明。重命名是为了方便后续的使用。友元声明则和迭代器的实现有关,这里先不过多讲解。

3.2 构造函数和析构函数

注意,这里没有实现拷贝构造。并不是不需要实现,实际上,虽然哈希桶内的成员是自定义类型的,会去调自己的拷贝构造,但是这里只会进行浅拷贝,不满足需要。如果有需要,可以自己实现拷贝构造,实现起来也很简单。

HashBucket():_n(0)
{_bucket.resize(10);//构建时默认开10个空间
}~HashBucket()//析构函数
{for (auto& cur : _bucket){while (cur){Node* prev = cur;cur = cur->_next;delete prev;prev = nullptr;}}
}

3.3 数据插入

insert()函数返回的是pair<iterator, bool>,这是为了后续实现 [ ] 重载做准备。

在插入时,首先要先查看哈希桶中是否存在相同键值,存在就直接返回当前位置。第二步就是要查看哈希桶中的元素个数与哈希桶的容量之间的负载因子,如果等于1,就需要进行扩容。第三步则是开始插入节点。先找到映射位置,然后新建一个节点连接到对应的数组下标的空间中即可

pair<iterator, bool> insert(const T& data)//插入数据
{KeyOfT kt;iterator it = find(kt(data));if (it != end())//不允许数据重复,找到相同的返回return make_pair(it, false);if (_bucket.size() == _n)//负载因子设置为1,超过就扩容{vector<Node*> newbucket;//这种方式就无需再开空间拷贝节点newbucket.resize(NextPrime(_bucket.size()));//开一个素数大小的空间for (size_t i = 0; i < _bucket.size(); ++i){Node* cur = _bucket[i];while (cur){Node* next = cur->_next;size_t hashi = Hash()(kt(cur->_data)) % newbucket.size();//找映射位置cur->_next = newbucket[hashi];//头插到新哈希表newbucket[hashi] = cur;cur = next;}_bucket[i] = nullptr;//将原哈希表中的指针置为空}_bucket.swap(newbucket);//将newbucket中的节点全部交换给_bucket}Hash knt;size_t hashi = knt(kt(data)) % _bucket.size();//找映射位置Node* newnode = new Node(data);newnode->_next = _bucket[hashi];//头插,让插入的节点的下一个指针指向头节点_bucket[hashi] = newnode;//让头节点指向插入的节点++_n;return make_pair(iterator(newnode, this), true);
}

3.4 数据查找

查找数据很简单。先通过hash函数计算出要查找的数据键值所对应的位置,如果对应的位置上存储的是空,说明不存在,直接返回。如果不为空,则比对键值,相同返回,不相同向下找直到为空。

iterator find(const K& key)//查找
{size_t pos = Hash()(key) % _bucket.size();//找映射位置	Node* cur = _bucket[pos];while (cur){if (KeyOfT()(cur->_data) == key)return iterator(cur, this);elsecur = cur->_next;}return end();
}

3.5 数据删除

哈希桶与哈希表不同,要删除数据时不能使用find()函数搜索。因为哈希桶中的数据是用单链表链接起来的,用find()就无法知道节点的父节点,进而无法链接链表。

因此,在删除时,首先要调用hash函数获取关键码,根据关键码蛆对应位置上找。如果该位置存的数据为空,则表示不存在,返回;如果不为空,就比较键值,相同为找到,删除,不同就继续向下找直到为空。

bool erase(const K& key)//删除
{size_t hashi = Hash()(key) % _bucket.size();//找映射位置Node* cur = _bucket[hashi];Node* prev = nullptr;while (cur){if (KeyOfT()(cur->_data) == key){if (cur == _bucket[hashi])_bucket[hashi] = cur->_next;elseprev->_next = cur->_next;delete cur;--_n;cur = nullptr;return true;}else{prev = cur;cur = cur->_next;}}return false;
}

3.6 迭代器实现

哈希桶中的迭代器实现方式比较复杂。要使用迭代器,首先就要有能够遍历哈希桶的手段。因此,为了便于遍历,迭代器的结构体中首先要有哈希桶的指针。当然,迭代器的结构体中还需要有一个数据的指针,用于初始化迭代器,获取对应位置上的内容。

要遍历哈希桶很简单,和find()的逻辑差不多。直接判断当前节点是否为空,不为空则返回;为空就说明当前下标对应的位置上已经没有数据了,就调用hash函数获取该节点的关键码。++关键码走向下一个下标,不为空则返回;为空则继续走,直到找到不为空的位置或结束。

如上图所示,一个哈希表,其中有四个哈希桶,迭代器是it。

++it操作:

  • 如果it不是某个桶的最后一个元素,则it指向下一个节点。
  • 如果it是桶的最后一个元素,则it指向下一个桶的头节点。
template<class K, class T, class Hash, class KeyOfT>
class HashBucket;//前置声明,因为迭代器中使用了HashBucket的模板,但是迭代器在它之前无法找到,所以要前置声明template<class K, class T, class Hash, class KeyOfT>
struct HashIterator
{typedef HashNode<T> Node;typedef HashIterator<K, T, Hash, KeyOfT> Self;typedef HashBucket<K, T, Hash, KeyOfT> HB;//将哈希桶传进来HashIterator(Node* node, HB* hb):_node(node),_hb(hb){}T& operator*()//解引用{return _node->_data;}T* operator->()//运算符->重载{return &_node->_data;}bool operator!=(const Self& sl) const//判断是否相等{return _node != sl._node;}Self operator++()//运算符++重载{if (_node->_next)//节点的下一个位置不为空{_node = _node->_next;}else//节点的下一个位置为空,说明该位置上已经没有值,找下一个不为空的桶节点{KeyOfT kt;size_t hashi = Hash()(kt(_node->_data)) % _hb->_bucket.size();++hashi;while (hashi < _hb->_bucket.size())//当前位置小于桶的数量{if (_hb->_bucket[hashi])//如果hashi位置上不为空,则修改_node的值{_node = _hb->_bucket[hashi];break;}else++hashi;}if (hashi == _hb->_bucket.size())//如果hashi的位置与桶的数量相当,说明没有找到_node = nullptr;}return *this;}Node* _node;//节点指针HB* _hb;//哈希桶的指针
};

注意,因为模板是向上寻找的,在迭代器的类模板中使用了哈希桶的类模板,所以如果迭代器类模板在哈希桶类模板之上,就需要进行类声明,让迭代器类模板能找到哈希桶类模板的位置。反之亦然。

注意,因为迭代器中传入了哈希桶,要对哈希桶进行遍历。但因为哈希桶中的成员变量都被设置为了私有,所以可以将迭代器声明为哈希桶的友元类,也可以单独提供一个获取哈希桶指针的函数。

有了迭代器的类模板后,哈希桶的迭代器实现起来就很轻松了。

iterator begin()
{for (size_t i = 0; i < _bucket.size(); ++i){if (_bucket[i]){return iterator(_bucket[i], this);}}return iterator(nullptr, this);
}iterator end()
{return iterator(nullptr, this);
}

注意,哈希桶的begin()要返回的是第一个不为空的桶,而不是第一个节点

3.7、友元声明

在++迭代器的时候,会使用到哈希表指针,哈希表指针又会使用到HashTable中的_tables。

  • HashTable中的_tables是私有成员,在类外是不能访问的。

解决这个问题可以在HashTable中写一个公有的访问函数,也可以采用友元,本喵这里就是使用的友元的方式。

  • 类模板的友元声明需要写模板参数,在类名前面加friend关键字,如上图绿色框中所示。

迭代器中的其他操作,如解引用,箭头,以及相等等运算符的重载本喵就不再详细介绍了,后面本喵会附源码,直接看代码即可。

四、unordered_map封装

#pragma once
#include "HashTable.h"
namespace lyl
{template<class K, class V, class Hash = HashFunc<K>>class unordered_map{public:struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;typedef typename HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT, 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);}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}iterator find(const K& key){return _ht.Find(key);}bool erase(const K& key){return _ht.Erase(key);}private:HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;};}}

五、unordered_set封装

#pragma once#include "HashTable.h"namespace lyl
{template<class K, class Hash = HashFunc<K>>class unordered_set{public:struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename HashBucket::HashTable<K, K, SetKeyOfT, Hash>::const_iterator iterator;typedef typename HashBucket::HashTable<K, K, SetKeyOfT, 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 K& key){return _ht.Insert(key);}iterator find(const K& key){return _ht.Find(key);}bool erase(const K& key){return _ht.Erase(key);}private:HashBucket::HashTable<K, K, SetKeyOfT, Hash> _ht;};}

六、哈希表

#pragma once
#include <vector>
#include<iostream>namespace OpenAddress
{enum State{EMPTY,EXIST,DELETE};template<class K, class V>struct HashData{pair<K, V> _kv;State _state = EMPTY;};template<class K, class V>class HashTable{public:bool Insert(const pair<K, V>& kv){if (Find(kv.first))return false;// 负载因子超过0.7就扩容//if ((double)_n / (double)_tables.size() >= 0.7)if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7){//size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;//vector<HashData> newtables(newsize);遍历旧表,重新映射到新表//for (auto& data : _tables)//{//	if (data._state == EXIST)//	{//		// 重新算在新表的位置//		size_t i = 1;//		size_t index = hashi;//		while (newtables[index]._state == EXIST)//		{//			index = hashi + i;//			index %= newtables.size();//			++i;//		}//		newtables[index]._kv = data._kv;//		newtables[index]._state = EXIST;//	}//}//_tables.swap(newtables);size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;HashTable<K, V> newht;newht._tables.resize(newsize);// 遍历旧表,重新映射到新表for (auto& data : _tables){if (data._state == EXIST){newht.Insert(data._kv);}}_tables.swap(newht._tables);}size_t hashi = kv.first % _tables.size();// 线性探测size_t i = 1;size_t index = hashi;while (_tables[index]._state == EXIST){index = hashi + i;index %= _tables.size();++i;}_tables[index]._kv = kv;_tables[index]._state = EXIST;_n++;return true;}HashData<K, V>* Find(const K& key){if (_tables.size() == 0){return 0;}size_t hashi = key % _tables.size();// 线性探测size_t i = 1;size_t index = hashi;while (_tables[index]._state != EMPTY){if (_tables[index]._state == EXIST&& _tables[index]._kv.first == key){return &_tables[index];}index = hashi + i;index %= _tables.size();++i;// 如果已经查找一圈,那么说明全是存在+删除if (index == hashi){break;}}return nullptr;}bool Erase(const K& key){HashData<K, V>* ret = Find(key);if (ret){ret->_state = DELETE;--_n;return true;}else{return false;}}private:vector<HashData<K, V>> _tables;size_t _n = 0; // 存储的数据个数//HashData* tables;//size_t _size;//size_t _capacity;};
}template<class K>
struct HashFunc
{size_t operator()(const K& key){return key;}
};// 特化
template<>
struct HashFunc<string>
{// BKDRsize_t operator()(const string& s){size_t hash = 0;for (auto ch : s){hash += ch;hash *= 31;}return hash;}
};namespace HashBucket
{template<class T>struct HashNode{HashNode<T>* _next;T _data;HashNode(const T& data):_next(nullptr), _data(data){}};// 前置声明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 __HashIterator{typedef HashNode<T> Node;typedef HashTable<K, T, KeyOfT, Hash> HT;typedef __HashIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;typedef __HashIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;Node* _node;const HT* _ht;__HashIterator(Node* node, const HT* ht):_node(node), _ht(ht){}__HashIterator(const Iterator& it):_node(it._node), _ht(it._ht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){if (_node->_next != nullptr){_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 KeyOfT, class Hash>class HashTable{template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>friend struct __HashIterator;typedef HashNode<T> Node;public:typedef __HashIterator<K, T, T&, T*, KeyOfT, Hash> iterator;typedef __HashIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;iterator begin(){Node* cur = nullptr;for (size_t i = 0; i < _tables.size(); ++i){cur = _tables[i];if (cur){break;}}return iterator(cur, this);}iterator end(){return iterator(nullptr, this);}const_iterator begin() const{Node* cur = nullptr;for (size_t i = 0; i < _tables.size(); ++i){cur = _tables[i];if (cur){break;}}return const_iterator(cur, this);}const_iterator end() const{return const_iterator(nullptr, this);}~HashTable(){for (auto& cur : _tables){while (cur){Node* next = cur->_next;delete cur;cur = next;}cur = nullptr;}}iterator Find(const K& key){if (_tables.size() == 0)return end();KeyOfT kot;Hash hash;size_t hashi = hash(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){Hash hash;KeyOfT kot;size_t hashi = hash(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;return true;}else{prev = cur;cur = cur->_next;}}return false;}// 休息一下:15:55继续// size_t newsize = GetNextPrime(_tables.size());size_t GetNextPrime(size_t prime){// SGIstatic 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};size_t i = 0;for (; i < __stl_num_primes; ++i){if (__stl_prime_list[i] > prime)return __stl_prime_list[i];}return __stl_prime_list[i];}pair<iterator, bool> Insert(const T& data){KeyOfT kot;iterator it = Find(kot(data));if (it != end()){return make_pair(it, false);}Hash hash;// 负载因因子==1时扩容if (_n == _tables.size()){/*size_t newsize = _tables.size() == 0 ? 10 : _tables.size()*2;HashTable<K, V> newht;newht.resize(newsize);for (auto cur : _tables){while (cur){newht.Insert(cur->_kv);cur = cur->_next;}}_tables.swap(newht._tables);*///size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;size_t newsize = GetNextPrime(_tables.size());vector<Node*> newtables(newsize, nullptr);//for (Node*& cur : _tables)for (auto& cur : _tables){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.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), false);;}size_t MaxBucketSize(){size_t max = 0;for (size_t i = 0; i < _tables.size(); ++i){auto cur = _tables[i];size_t size = 0;while (cur){++size;cur = cur->_next;}//printf("[%d]->%d\n", i, size);if (size > max){max = size;}}return max;}private:vector<Node*> _tables; // 指针数组size_t _n = 0; // 存储有效数据个数};
}

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

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

相关文章

STM32学习笔记三——深度讲解GPIO及其应用

目录 STM32GPIO端口位基本结构图&#xff1a; 结构图I/O引脚&#xff1a; GPIO输入输出总结 1.GPIO引脚的四种输入方式及其特点&#xff1a; 1)上拉输入(GPIO_Mode_IPU) 2)下拉输入(GPIO_Mode_IPD) 3)模拟输入(GPIO_Mode_AIN) 4)浮空输入(GPIO_Mode_IN_FLOATING…

【git】本地项目推送到github、合并分支的使用

1. github上创建仓库信息 点击个人头像&#xff0c;选择【你的仓库】 点击【新增】 填写仓库信息 2. 本地项目执行的操作 1.生成本地的git管理 (会生成一个.git的文件夹) git init 2.正常提交到暂存区&#xff0c;并填写提交消息 git add . git commit -m "init…

彻底学会系列:一、机器学习之线性回归

1.基本概念 线性回归&#xff1a; 有监督学习的一种算法。主要关注多个因变量和一个目标变量之间的关系。 因变量&#xff1a; 影响目标变量的因素&#xff1a; X 1 , X 2 . . . X_1, X_2... X1​,X2​... &#xff0c;连续值或离散值。 目标变量&#xff1a; 需要预测的值: t…

Openresty+Lua+Redis实现高性能缓存

一、背景 当我们的程序需要提供较高的并发访问时&#xff0c;往往需要在程序中引入缓存技术&#xff0c;通常都是使用Redis作为缓存&#xff0c;但是要再更进一步提升性能的话&#xff0c;就需要尽可能的减少请求的链路长度&#xff0c;比如可以将访问Redis缓存从Tomcat服务器…

MQ面试题整理(持续更新)

1. MQ的优缺点 优点&#xff1a;解耦&#xff0c;异步&#xff0c;削峰 缺点&#xff1a; 系统可用性降低 系统引入的外部依赖越多&#xff0c;越容易挂掉。万一 MQ 挂了&#xff0c;MQ 一挂&#xff0c;整套系统崩 溃&#xff0c;你不就完了&#xff1f;系统复杂度提高 硬生…

ES高可用架构涉及常用功能整理

ES高可用架构涉及常用功能整理 1. es的高可用系统架构和相关组件2. es的核心参数2.1 常规配置2.2 特殊优化配置2.2.1 数据分片按ip打散2.2.2 数据分片机架感知2.2.3 强制要求数据分片机架感知2.2.4 写入线程池优化2.2.5 分片balance优化2.2.6 限流控制器优化 3. es常用命令3.1 …

前缀和 acwing

思路&#xff1a;两个数组&#xff0c;一个数组用来保存数据&#xff0c;一个数组来求对应项的和 前缀和S[r]-s[r-1] 空出来下标0 从1开始 方便表示&#xff0c;防止越界 c代码实现: #include<iostream> using namespace std; const int N1000000; int a[N],s[N]; …

344. Reverse String(反转字符串)

题目描述 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 问题分析 以中间字符为轴&#xff0c;将两边的字符对换…

CSS-IN-JS

CSS-IN-JS 为什么会有CSS-IN-JS CSS-IN-JS是web项目中将CSS代码捆绑在JavaScript代码中的解决方案。 这种方案旨在解决CSS的局限性&#xff0c;例如缺乏动态功能&#xff0c;作用域和可移植性。 CSS-IN-JS介绍 1&#xff1a;CSS-IN-JS方案的优点&#xff1a; 让css代码拥…

Java与SpringBoot:实现高效车险理赔信息管理系统

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

ArcGIS学习(三)数据可视化

ArcGIS学习(三)数据可视化 1.矢量数据可视化 需要提前说明的是,在ArcGIS中,所有的可视化选项设置都是在“图层属性”对话框里面的“符号系统”中实现的。 对于矢量数据的可视化,主要有四种可视化方式: 按“要素”可视化按“类别”可视化按“数量”可视化按“图表”可视…

【Elasticsearch】从入门到精通

目前java常见的针对大数据存储的方案并不多&#xff0c;常见的就是mysql的分库分表、es存储 这里偏向es存储方案&#xff0c;es不同的版本之间其实差异还挺大的&#xff0c;本篇博文版本Elasticsearch 7.14.0 Springboot整合Easy-Es Easy-Es官方文档 Elasticsearch的初步认识 …

机器翻译后的美赛论文怎么润色

美赛论文的语言表达一直是组委会看重的点&#xff0c;清晰的思路和地道的语言在评审中是重要的加分项。 今天我们就来讲讲美赛论文的语言问题。 我相信有相当一部分队伍在打美赛的时候&#xff0c;出于效率的考量&#xff0c;都会选择先写中文论文&#xff0c;再机翻成英文。 …

【蓝桥杯冲冲冲】[NOIP2003 普及组] 栈

蓝桥杯备赛 | 洛谷做题打卡day27 文章目录 蓝桥杯备赛 | 洛谷做题打卡day27题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示题解代码我的一些话 [NOIP2003 普及组] 栈 题目背景 栈是计算机中经典的数据结构&#xff0c;简单的说&#xff0c;栈就是限制在一…

Linux校准时间 Centos

Linux校准时间 Centos 首先&#xff0c;确保系统中已经安装了tzdata包。如果没有安装&#xff0c;可以使用以下命令安装&#xff1a; sudo yum install tzdata设置系统时区为上海&#xff1a; sudo timedatectl set-timezone Asia/Shanghai验证时区设置是否生效&#xff1a;…

图解支付-金融级密钥管理系统:构建支付系统的安全基石

经常在网上看到某某公司几千万的个人敏感信息被泄露&#xff0c;这要是放在持牌的支付公司&#xff0c;可能就是一个非常大的麻烦&#xff0c;不但会失去用户的信任&#xff0c;而且可能会被吊销牌照。而现实情况是很多公司的技术研发人员并没有足够深的安全架构经验来设计一套…

RK3568平台 设备模型基本框架-kobject 和kset

一.什么是设备模型 字符设备驱动通常适用于相对简单的设备&#xff0c;对于一些更复杂的功能&#xff0c;比如说电源管理和热插拔事件管理&#xff0c;使用字符设备框架可能不够灵活和高效。为了应对更复杂的设备和功能&#xff0c;Linux内核提供了设备模型。设备模型允许开发…

Centos7安装图形界面并使用Win10远程桌面连接

Centos7安装图形界面并使用Win10远程桌面连接 一、关闭防火墙和selinux二、安装Server with GUI三、设置系统默认启动进入GUI界面四、安装epel库和xrdp五、启动xrdp并设置成开机自启六、win10电脑打开连接 一、关闭防火墙和selinux #临时关闭防火墙 [rootse1215 ~]# systemctl…

WordPress从入门到精通【安装部署】

初识WordPress WordPress&#xff0c;简称WP&#xff0c;其简称的由来是取英文单词“word”与“press”的首字母 WP中文官网 1WP主站&#xff08;英文&#xff09; 官方标称&#xff0c;已有43%的网站在使用WordPress WordPress亮点 WP使用PHP语言开发&#xff0c;兼容性极…

python推荐算法在汽车用品商城营销系统 django+flask

本论文拟采用计算机技术设计并开发的汽车营销中的设计与实践 &#xff0c;主要是为用户提供服务。使得会员可以在系统上查看汽车商品、汽车快讯、还可以咨询客服&#xff0c;管理员对信息进行统一管理&#xff0c;与此同时可以筛选出符合的信息&#xff0c;给笔者提供更符合实际…