当前位置: 首页 > wzjs >正文

礼物说网站模板微信公众号怎么开通

礼物说网站模板,微信公众号怎么开通,商丘市网站建设公司,vps挂网站目录 1. 常见的容器及其常见问题 1.1 序列式容器 1.1.1 常见问题: 1. string、vector 和 list 的区别? 2. string、vector、list是怎么插入数据的?如何扩容的? 1.2 关联式容器 1.2.1 树形结构的关联式容器 map / set&…

目录

1. 常见的容器及其常见问题

1.1 序列式容器

1.1.1 常见问题:

1. string、vector 和 list 的区别?

2. string、vector、list是怎么插入数据的?如何扩容的?

1.2 关联式容器

1.2.1 树形结构的关联式容器

map / set:

1. 底层是什么:树形结构的这4种容器的底层都是红黑树。

2. 一个类型做 map/set 的 key 有什么要求?

3. map的operator[ ]功能是什么?如何实现?

multi_map / multi_set:

1.与各自 map 和 set 版本的不同:

1.2.2 哈希结构的关联式容器

unordered_map / unordered_set:(其实还有unordered_multimap/unordered_multiset)

1. 如何实现的:哈希表

2. unordered_map和map的区别

3. unordered_set 和 set 的区别

4. 一个类型做 unordered_map / unordered_set 有什么要求?

1.3 容器适配器


 

1. 常见的容器及其常见问题

1.1 序列式容器

        对应线性结构,逻辑上是依次存储的,任意位置都能插入,string和vector用的是最多的。string 严格来说虽然不属于STL,但是也比较重要,所以囊括进来做对比。

string:07.string类_stding::length()-CSDN博客

vector:08.vector-CSDN博客

list:09.list-CSDN博客

1.1.1 常见问题:

1. string、vector 和 list 的区别?

用法:

  • string是专门用于处理字符串的容器,string后有 \0,string s
  • vector是动态数组,数组元素类型任意vector<string>、vector<int>...
  • list是双向链表,list<int>

优点:

  • string 支持运算符重载(+, +=等),使用直观;专为字符串设计,接口丰富(find, substr等)
  • vecto支持随机访问O(1);尾插、尾删高效
  • list 任意位置插入删除高效;没有容量概念,按需分配节点;插入删除不会使迭代器失效(除了被删除元素)

缺点:

  • string 某些操作(如插入/删除非末尾字符)效率不高
  • vector 非尾部插入/删除效率低O(n);容量不足时需要重新分配内存并拷贝元素
  • list 不支持随机访问(访问元素O(n)),[ ]会从开头遍历到指定位置;内存开销大(每个元素需要额外存储前后指针)

底层实现:

  • string 通常实现为动态数组,char* _str,size_t _size,size_t _capacity。
  • vector 维护三个指针:起始位置、当前元素末尾、容量末尾:
            typedef T* iterator;iterator _start;iterator _finish;iterator _endofstorage;
  •  list 使用双向链表节点结构,每个节点包含数据和前后指针,通常实现为循环链表,有哨兵头节点head
    namespace zzy
    {template<class T>struct list_node//为什么这里写它?{list_node<T>* _next;list_node<T>* _prev;T _val;explicit list_node(const T& val = T()):_next(nullptr),_prev(nullptr),_val(val){}};template<class T>class list{typedef list_node<T> Node;public:list(){_head = new Node;_head->_prev = _head;_head->_next = _head;_size = 0;}private:Node* _head;size_t _size;};
    }
2. string、vector、list是怎么插入数据的?如何扩容的?

string:

        插入有两种,插入字符、字符串,分成push_back和append,同理在插入时要先检查空间扩容,特色的operate+=就根据参数是char或char*,分别调用push_back和append。

        void reserve(size_t n){if(n > _capacity){char* tmp = new char[n+1];memcpy(tmp, _str, _size+1);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if(_size == _capacity){reserve(_capacity == 0 ? 4: _capacity*2);}_str[_size] = ch;++_size;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);//如果插入后的大小大于容量,二倍的容量不一定有size+len大if(_size + len > _capacity){reserve(_size+len);}memcpy(_str+_size, str, len+1);_size += len;}string& operator+=(char ch){push_back(ch);return *this;}string operator+=(const char* str){append(str);return *this;}

vector:

        尾插的话就先判断容量,不够就扩容,新元素放在最后一个位置上,finish++。

        如果是insert,也是先判断容量,扩容后重新找到要插入的位置(迭代器失效),从尾向前搬移元素,新元素放到pos上,finish++。

        void push_back(const T& x){if(_finish == _endofstorage){size_t newcapacity = capacity() == 0? 4:capacity*2;reserve(newcapacity);}*_finish = x;++_finish;}

        如果要扩的容量大于当前的容量,就扩容,注意保留原大小以及逐个拷贝,不能使用memcpy(涉及到内存管理,memcpy只会复制指针值,不会复制指针指向的内容)

        void reserve(size_t n){if(n > capacity()){size_t sz = size();T* tmp = new T[n];if(_start){//memcpy(tmp, _start, sizeof(T)*sz);for (size_t i=0; i<sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage  = _start + n;}}

list:

        其实就是双向带头循环链表的插入,没有扩容一说。

1.2 关联式容器

        关联式容器也是用来存储数据的,与序列式容器不同的是,里面存的是<key,value>结构的键值对,在数据检索时比序列式容器效率更高

1.2.1 树形结构的关联式容器

map / set:
1. 底层是什么:树形结构的这4种容器的底层都是红黑树。

各自的规则:

set:

  • set与map/multimap 不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value,value>构成的键值对。
  • set按一定次序存储元素,默认按小于来比较,查找某个元素时,时间复杂度为O(logN)。
  • set中的元素不允许修改,因为用的都是const迭代器。可以从容器中插入和删除。

map:

  • map存储按特定次序(按key比较)由键和值组合而成的元素,为其取别名pair<>
  • key也不允许修改,用的是const key
  • 支持下标访问符,即在[ ]中放入key,可以找到与key对应的value
  • 当key元素不存在时,operator[]用默认的value与key构造键值对然后插入,返回默认value,at()直接抛异常
  • map中的元素如果用迭代器去遍历,可以得到一个有序的序列

效率:

        set 中通过键访问单个元素时比 unodered_set

        map 中通过键值访问单个元素时比 unodered_map

大概实现:

         map和set底层使用的都是红黑树,那怎么让map和set共用一颗红黑树的呢?

使用模板,首先需要对红黑树进行改造

template<class K>
class set
{
private:RBTree<K, K> _t;
};template<class K, class V>
class map
{
private:RBTree<K, pair<K, V>> _t;
};

        我们可以从上面的代码看出,set 和 map 的key位置一样,但是value位置,set用的是K,而map用的是一个pair<K, V>,他们是通过同一棵树搞出来的_t,那么RBTree在这个位置就要用一个模板T。

  • setRBTree<K, K>:因为 set 只存 Key,复用 RBTree 时需要传 Value=K

  • mapRBTree<K, pair<const K, V>>:因为 map 存键值对,但比较时只关心 Key

这样设计可以最大程度复用 RBTree,避免为 setmap 分别实现不同的红黑树,set迁就了map

        如下方修改后的tree,如果是set建树,传给模板T的是key,树的结点就会用这个T(key)去定义数据;如果是map建树,传给模板T的是pair<K, V>,树的结点就会用这个T(pair<K, V>)去定义数据。

template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Color _col;
};template<class K, class T>
class RBTree
{typedef RBTreeNode<T> Node;
};

        还有一个问题就是,set,map建树需要比较key,set比较的就是保存的值,map比较的是保存的键值对pair<key,value>key的值。可以看出来set的值可以直接比较,但是 map 需要通过对键值对进行解引用才能得到key的值,两者方式不同。

        我们可以通过仿函数+模板解决这个问题:

首先要再给RBTree添加一个模板,KeyOfT

template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
};

 然后在map和set中实现各自KeyOfT的类,类中实现一个仿函数,分别取出他们自己的key:

namespace zzy
{template<class K,class T>class map{public://仿函数来获取值struct MapKofT{const K& operator()(const pair<K, T>& V){return V.first;}};typedef typename RBTree<K, pair<K, T>, MapKofT>::Iterator iterator;pair<iterator, bool> Insert(const pair<K, T>& data){return _tree.insert(data);}iterator begin(){return _tree.begin();}iterator end(){return _tree.end();}T& operator[](const K& k){pair<iterator, bool> ret = Insert(pair<K, T>(k, T()));return ret.first->second;}private:RBTree<K, pair<K, T>, MapKofT> _tree{};};
}
namespace zzy
{template<class K>class set{public:struct SetKofT{const K& operator()(const K& k){return k;}};typedef typename RBTree<K, K, SetKofK>::Iterator iterator;pair<iterator, bool> Insert(const K& k){return _tree.insert(k);}iterator begin(){return _tree.begin();}iterator end(){return _tree.end();}private:RBTree<K, K, SetKofT> _tree{};};
}

可以看到他们在建树时也把自己求K的类传给了RBTree。

这时树中的class KeyOfT模板接收的就是他们各自传的 SetKofT 和 MapKofT。

template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:bool insert(const T& data){KeyOfT kot;while(cur){if(kot(cur->data) < kot(data)){parent = cur;cur = cur->_right;}//...}}
};

        传过来的这个类要在函数中实例化一个对象kot才能用对象去使用其中的仿函数,像上面的代码一样。当然其实是SetKofT 或 MapKofT 类的对象去调用各自的仿函数。

2. 一个类型做 map/set 的 key 有什么要求?

        :可以支持比大小 或者 提供仿函数比较大小

3. map的operator[ ]功能是什么?如何实现?

功能:它提供了对映射元素的快速访问和修改能力。

int main() 
{map<string, int> word_counts;// 插入新元素word_counts["apple"] = 5;  // 键"apple"不存在,自动插入// 修改现有元素word_counts["apple"] += 3; // 现在"apple"对应的值是8// 访问元素cout << "apple count: " << word_counts["apple"] << endl;// 访问不存在的键会创建新元素cout << "orange count: " << word_counts["orange"] << endl;// 现在map中包含"orange"键,值为0return 0;
}

实现:

        opeartor[ ]的原理是:用<key,T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中。如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器;如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器。operator[ ]函数最后将insert返回键值对中的value返回。(给key返回value)

        T& operator[](const K& k){pair<iterator, bool> ret = Insert(pair<K, T>(k, T()));return ret.first->second;}
  • 创建一个新的键值对pair<K, T>,其中:

    • 键是传入的k

    • 值是用T()构造的默认值(例如,如果Tint,则值为0)

  • return ret.first->second;

    • ret.firstinsert返回的迭代器

    • ->second访问迭代器指向的键值对中的值部分

    • 返回这个值的引用

multi_map / multi_set:
1.与各自 map 和 set 版本的不同:
特性map / setmultimap / multiset
元素唯一性键/元素必须唯一允许重复键/元素
插入行为key存在插入失败/忽略总是插入新元素
查找返回值返回单个迭代器可能返回多个元素/迭代器范围
典型实现红黑树红黑树
内存占用相对较小可能较大(存储重复元素)
查找count()返回0或1count()返回某元素数量
// map示例
map<int, string> m;
m.insert({1, "a"});  // 成功
m.insert({1, "b"});  // 失败,键1已存在
m[1] = "b";          // 覆盖原有值// multimap示例
multimap<int, string> mm;
mm.insert({1, "a"});  // 成功
mm.insert({1, "b"});  // 成功,允许重复键
// mm[1] = "c";       // 错误,multimap不支持operator[]

multimap不支持operator[ ]

如果operator[]用于multimap,当键重复时,无法明确应该返回哪个值的引用,导致语义模糊。

multimap<int, string> m;
m.insert({1, "a"});
m.insert({1, "b"});
string val = m[1]; // 应该返回 "a" 还是 "b"?

1.2.2 哈希结构的关联式容器

        最好的查询是,进行很少的比较次数就能将元素找到,所以C++11中,STL又提供了4个unordered系列的关联式容器,与红黑树结构的使用类似,只是底层结构不同。

unordered_map / unordered_set:(其实还有unordered_multimap/unordered_multiset)
1. 如何实现的:哈希表

规则:

        unordered_map 中 key 是不能重复的,因此count函数的返回值最大为1。

        unordered_map 的 operator[ ],实际调用哈希桶的插入操作,用参数key和V()构造一个默认值往底层哈希桶插入,遍历桶内的元素,用 keyoperator== 进行比较,找到匹配的 key,如果key不在哈希桶中,插入成功;如果找到:返回对应的 value

大概实现:

  • 底层实现:基于 指针数组 + 链表/红黑树(解决冲突),数组的每个槽位称为 桶,存储链表的头指针。

  • 通过 哈希函数 计算 Key 的哈希值映射到桶数组的某个位置以完成增删查。

  • 当载荷因子到1时扩容到二倍,遍历所有元素,重新计算哈希并放入桶中。

2. unordered_map和map的区别
特性std::mapstd::unordered_map
数据结构红黑树(RB-Tree)哈希表(Hash Table)(数组 + 链表/红黑树)
是否有序(按键 Key 升序排列)(元素存储顺序取决于哈希函数)
是否需要 operator<必须定义(用于排序)不需要
是否需要 hashoperator==不需要必须定义(用于哈希计算和冲突检测)
内存占用较低较高(桶数组 + 链表/红黑树)

 时间复杂度:

操作std::map (红黑树)std::unordered_map (哈希表)
查找 find()O(log n)平均 O(1),最坏 O(n)(哈希冲突严重时)
插入 insert()O(log n)平均 O(1),最坏 O(n)
删除 erase()O(log n)平均 O(1),最坏 O(n)
遍历 iteration有序遍历(按 Key 排序)无序遍历(顺序取决于哈希桶)
3. unordered_set 和 set 的区别

        和上面的差不多,也是红黑树-哈希表、有序-无序、O(log n)-O(1)、内存占用较高-较低

4. 一个类型做 unordered_map / unordered_set 有什么要求?
  • unordered_mapunordered_set 基于哈希表实现,因此必须能够计算 Key 的哈希值。
  • 必须提供相等比较(operator==),需要通过 operator== 判断两个 Key 是否真正相等。

1.3 容器适配器

10.stack 和 queue_queue 和 stack-CSDN博客

http://www.dtcms.com/wzjs/415516.html

相关文章:

  • 销售网站开发北京广告公司
  • 网站机房建设成本免费推广app
  • 用户体验设计流程网络公司优化关键词
  • 平台网站做代理商网站建设方案书 模板
  • 2015做外贸网站好做吗seo排名怎么样
  • 黄骅做网站_黄骅昊信科技|黄骅网站|黄骅网站开发|黄骅微信|黄骅有没有免费的广告平台
  • 没有货源可以开网店吗扬州整站seo
  • 偷拍网站做免费注册个人网站
  • 公司网站设计与制互联网推广项目
  • ubuntu下做网站seo扣费系统
  • 天津建设工程信息网专家灯塔网站seo
  • 高端网站建设哪家更专业建网站的软件有哪些
  • 哈尔滨网站建设吕新松微信管理系统登录
  • 网站建设及维护专业数据分析师要学什么
  • 如何做一名优秀的网站管理者小程序推广运营的公司
  • 昆明网站建设在河科技广州今日头条新闻
  • 苏州网站开发建设服务免费推广产品平台有哪些
  • 使用wordpress做网站电商数据统计网站
  • 张家口网站开发志鸿优化网
  • 做网站要服务器和什么软件如何销售自己产品方法有哪些
  • 网站排名优化课程seo快速排名是什么
  • 怎么做网站的需求旅行网站排名
  • wordpress不小心改了网站地址怎么做好市场宣传和推广
  • 网站数据分析的重要性广州搜索排名优化
  • 环境艺术设计最好的大学如何优化百度seo排名
  • 网站前台与后台建设的先后次序推广运营怎么做
  • 手机网站建设软件百度竞价被换着ip点击
  • 易语言可以做api网站对接吗百度搜索网页
  • 公司做网站一般多少钱运营软文写作服务
  • 建设景区网站的目的如何提高百度关键词排名