文章目录

  • 一、string容器
  • 二、vector容器
  • 三、list容器
    • 1.构造函数
    • 2.特性操作
    • 3.元素操作
    • 4.赋值操作
    • 5.交换、反转、排序、归并
    • 6.比较操作
  • 7.插入和删除
  • 四、pair键值对
  • 五、map容器
    • 1.红黑树(平衡二叉排序树)
    • 2.构造函数
    • 3.特性操作
    • 4.元素操作
    • 5.赋值操作
    • 6.交换操作
    • 7.比较操作
    • 8.查找操作
    • 9.插入和删除
  • 六、unordered_map容器
    • 1.哈希表
    • 2.构造函数
    • 3.特性操作
    • 4.元素操作
    • 5.赋值操作
    • 6.交换操作
    • 7.比较操作
    • 8.查找操作
    • 9.插入和删除
  • 六、queue容器
    • 1.构造函数
    • 2.常用操作
    • 3.其他操作
  • 七、STL其他容器
    • 1.array(静态数组)
    • 2.deque(双端队列)
    • 3.forward_list(单链表)
    • 4.multimap
    • 5.set&multiset
    • 6.unordered_multimap
    • 7.unordered_set&unordered_multiset
    • 8.priority_queue(优先队列)
    • 9.stack(栈)
    • 10.std::hash

一、string容器

二、vector容器

三、list容器

1.构造函数

  • 本质是双向链表
1)list();  // 创建一个空的list容器。
2)list(initializer_list<T> il); // 使用统一初始化列表。
3)list(const list<T>& l);  // 拷贝构造函数。
4)list(Iterator first, Iterator last);  // 用迭代器创建list容器。
5)list(list<T>&& l);  // 移动构造函数(C++11标准)。不常用:
6)explicit list(const size_t n);   // 创建list容器,元素个数为n。
7)list(const size_t n, const T& value);  // 创建list容器,元素个数为n,值均为value。
  • eg:
#include <iostream>
#include <vector>
#include <list>
using  namespace std;int main()
{// 1)list();  // 创建一个空的list容器。list<int> l1;// cout << "li.capacity()=" << l1.capacity() << endl;  // 链表没有容量说法。不需要提前分配内存cout << "li.size()=" << l1.size() << endl;// 2)list(initializer_list<T> il); // 使用统一初始化列表。list<int> l2({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });// list<int> l2={ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };// list<int> l2  { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };for (int value : l2)       // 用基于范围的for循环遍历容器。cout << value << " ";cout << endl;// 3)list(const list<T>& l);  // 拷贝构造函数。list<int> l3(l2);// list<int> l3=l2;for (int value : l3)    cout << value << " ";cout << endl;// 4)list(Iterator first, Iterator last);  // 用迭代器创建list容器。//list<int> l4(l3.begin(), l3.end()-3);  error,不支持随机访问:list<int> l4(l3.begin(), l3.end());      // 用list容器的迭代器。for (int value : l4)  cout << value << " ";cout << endl;vector<int> v1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };  // 创建vector容器。list<int> l5(v1.begin() + 2, v1.end() - 3);          // 用vector容器的迭代器创建list容器。for (int value : l5)   cout << value << " ";cout << endl;int a1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };   // 创建数组。数组的指针是天然的随机访问迭代器//    list<int> l6(std::begin(a1) + 2, std::end(a1) + 10 - 3);   list<int> l6(a1 + 2, a1 + 10 - 3);           // 用数组的指针作为迭代器创建list容器。for (int value : l6)     cout << value << " ";cout << endl;char str[] = "hello world";         // 定义C风格字符串。string s1(str + 1, str + 7);          // 用C风格字符串创建string容器。for (auto value : s1)                   // 遍历string容器。cout << value << " ";cout << endl;cout << s1 << endl;                 // 以字符串的方式显示string容器。vector<int> v2(l3.begin(), l3.end());   // 用list迭代器创建vector容器。for (auto value : v2)                               // 遍历vector容器。cout << value << " ";cout << endl;
}

2.特性操作

size_t size() const;        // 返回容器的实际大小(已使用的空间)。
bool empty() const;      // 判断容器是否为空。
void clear();             // 清空容器。
void resize(size_t size);   // 把容器的实际大小置为size。不常用:
size_t max_size() const;     // 返回容器的最大长度,此函数意义不大。
void resize(size_t size,const T &value);  // 把容器的实际大小置为size,如果size<实际大小,会截断多出的部分;如果size>实际大小,就用value填充。

3.元素操作

T &front();        // 第一个元素。
const T &front();  // 第一个元素,只读。
const T &back();  // 最后一个元素,只读。
T &back();        // 最后一个元素。

4.赋值操作

给已存在的容器赋值,将覆盖容器中原有的内容。

1)list &operator=(const list<T> &l);         // 把容器l赋值给当前容器。
2)list &operator=(initializer_list<T> il);  // 用统一初始化列表给当前容器赋值。
3)list assign(initializer_list<T> il);        // 使用统一初始化列表赋值。
4)list assign(Iterator first, Iterator last);  // 用迭代器赋值。不常用:
5)void assign(const size_t n, const T& value);  // 把n个value给容器赋值。

5.交换、反转、排序、归并

void swap(list<T> &l);   // 把当前容器与l交换,交换的是链表结点的地址。
void reverse();           // 反转链表。
void sort();              // 对容器中的元素进行升序排序。
void sort(_Pr2 _Pred);    // 对容器中的元素进行排序,排序的方法由_Pred决定(二元函数)。
void merge(list<T> &l);  // 采用归并法合并两个已排序的list容器,合并后的list容器仍是有序的。
  • eg:
#include <iostream>
#include <vector>
#include <list>
using  namespace std;int main()
{list<int>  la = { 8,2,6,4,5 };for (auto &val : la)cout << val << " ";cout << endl;la.reverse();      // 反转链表。for (auto& val : la)cout << val << " ";cout << endl;la.sort();            // 链表排序。for (auto& val : la)cout << val << " ";cout << endl;list<int>  lb = { 3,7,9,10,1 };lb.sort();            // 链表排序。la.merge(lb);    // 归并链表。for (auto& val : la)cout << val << " ";cout << endl;
}

6.比较操作

bool operator == (const vector<T> & l) const;
bool operator != (const vector<T> & l) const;

7.插入和删除

1)void push_back(const T& value);  // 在链表的尾部追加一个元素。
2)void emplace_back(…);           // 在链表的尾部追加一个元素,…用于构造元素。C++11
3)iterator insert(iterator pos, const T& value);  // 在指定位置插入一个元素,返回指向插入元素的迭代器。
4)iterator emplace (iterator pos, …);  // 在指定位置插入一个元素,…用于构造元素,返回指向插入元素的迭代器。C++11
5)iterator insert(iterator pos, iterator first, iterator last);  // 在指定位置插入一个区间的元素,返回指向第一个插入元素的迭代器。
6)void pop_back();                      // 从链表尾部删除一个元素。
7)iterator erase(iterator pos);             // 删除指定位置的元素,返回下一个有效的迭代器。
8)iterator erase(iterator first, iterator last); // 删除指定区间的元素,返回下一个有效的迭代器。
9)push_front(const T& value);  // 在链表的头部插入一个元素。
10)emplace_front(…);          // 在链表的头部插入一个元素,…用于构造元素。C++11
11)splice(iterator pos, const list<T> & l);      // 把另一个链表连接到当前链表。
12)splice(iterator pos, const list<T> & l, iterator first, iterator last); // 把另一个链表指定的区间连接到当前链表。
13)splice(iterator pos, const list<T> & l, iterator first);    // 把另一个链表从first开始的结点连接到当前链表。
14)void remove(const T& value);   // 删除链表中所有值等于value的元素。
15)void remove_if(_Pr1 _Pred);    // 删除链表中满足条件的元素,参数_Pred是一元函数。
16)void unique();                 // 删除链表中相邻的重复元素,只保留一个。
17)void pop_front();              // 从链表头部删除一个元素。
  • eg:
#include <iostream>
#include <vector>
#include <list>
using  namespace std;int main()
{list<int>  la = { 8,2,6,4,5 };for (auto& val : la)  cout << val << " ";cout << endl;list<int>  lb = { 3,7,9,10,1 };for (auto& val : lb)  cout << val << " ";cout << endl;auto first = lb.begin();first++;auto last = lb.end();last--;la.splice(la.begin(), lb, first, last);for (auto& val : la)     cout << val << " ";cout << endl;cout << "lb.size()=" << lb.size() << endl;for (auto& val : lb)     cout << val << " ";cout << endl;
}

四、pair键值对

pair是类模板,一般用于表示key/value数据,其实现是结构体。

  • pair结构模板的定义如下:
template <class T1, class T2>
struct pair
{ T1 first;     // 第一个成员,一般表示key。T2 second;  // 第二个成员,一般表示value。pair();       // 默认构造函数。pair(const T1 &val1,const T2 &val2);   // 有两个参数的构造函数。pair(const pair<T1,T2> &p);           // 拷贝构造函数。void swap(pair<T1,T2> &p);           // 交换两个pair。
};

make_pair函数模板的定义如下:

template <class T1, class T2>
make_pair(const T1 &first,const T2 &second)
{return pair<T1,T2>(first, second);
}
  • eg:
#include <iostream>
using  namespace std;template <class T1, class T2>
struct Pair
{T1 first;        // 第一个成员,一般表示key。T2 second;  // 第二个成员,一般表示value。Pair()  {cout << "调用了有默认的构造函数。\n";}Pair(const T1& val1, const T2& val2) :first(val1), second(val2)  {cout << "调用了有两个参数的构造函数。\n";}Pair(const Pair<T1, T2>& p) : first(p.first),second(p.second)  {cout << "调用了拷贝构造函数。\n";}
};template <class T1, class T2>
Pair<T1, T2> make_Pair(const T1& first, const T2& second)
{//下面两种写法,Linux平台都不会多调用一次拷贝,都是RVO优化,直接移动// Pair<T1, T2> p(first, second);// return p;        // 返回局部对象。return Pair<T1, T2>(first, second);  // 返回临时对象。
}int main()
{//pair<int, string> p0;//cout << "p0 first=" << p0.first << ",second=" << p0.second << endl;//pair<int, string> p1(1, "西施1");    // 两个参数的构造函数。//cout << "p1 first=" << p1.first << ",second=" << p1.second << endl;//pair<int, string> p2 = p1;             // 隐式构造。//cout << "p2 first=" << p2.first << ",second=" << p2.second << endl;//pair<int, string> p3 = { 3, "西施3" };   //隐式构造。 pair<int, string> p3 { 3, "西施3" };   // 两个参数的构造函数,省略了等于号。//cout << "p3 first=" << p3.first << ",second=" << p3.second << endl;auto p4 = Pair<int, string>(4, "西施4");   // 匿名对象(隐式构造)。cout << "p4 first=" << p4.first << ",second=" << p4.second << endl;auto p5 = make_Pair<int, string>(5, "西施5");   // make_pair()返回的临时对象。(隐式构造)cout << "p5 first=" << p5.first << ",second=" << p5.second << endl;//pair<int, string> p6 = make_pair(6, "西施6");  //隐式构造, 让make_pair()函数自动推导,再调用拷贝构造,再隐式转换。//cout << "p6 first=" << p6.first << ",second=" << p6.second << endl;//auto p7 = make_pair(7, "西施7");    // 隐式构造,让make_pair()函数自动推导。//cout << "p7 first=" << p7.first << ",second=" << p7.second << endl;//p5.swap(p4);   // 交换两个pair。//cout << "p4 first=" << p4.first << ",second=" << p4.second << endl;//cout << "p5 first=" << p5.first << ",second=" << p5.second << endl;//struct st_girl//{//    string name;//  int   age;//    double height;//}; 用pair存放结构体数据。//pair<int, st_girl> p = { 3,{"西施",23,48.6} };//cout << "p first=" << p.first << endl;//cout << "p second.name=" << p.second.name << endl;//cout << "p second.age=" << p.second.age << endl;//cout << "p second.height=" << p.second.height << endl;
}

五、map容器

1.红黑树(平衡二叉排序树)

红黑树:左边节点比自己小,右边的节点比自己大

  • 红黑树查找元素的方法,又称之为二分查找

  • eg:跟节点比左子树的任意一个节点都大,比右子树的任意一个节点都小

  • h:高度

  • 物理结构:二叉链表

struct BTNode{pair<K,V> p;//键值对BTNode* parent;//父节点BTNOde* lchirld;//左子树BTNode* * rchild;//右子树
};

2.构造函数

1)map();  // 创建一个空的map容器。
2)map(initializer_list<pair<K,V>> il); // 使用统一初始化列表。
3)map(const map<K,V>& m);  // 拷贝构造函数。
4)map(Iterator first, Iterator last);  // 用迭代器创建map容器。
5)map(map<K,V>&& m);  // 移动构造函数(C++11标准)。
  • eg:
#include <iostream>
#include <map>
using  namespace std;int main()
{// 1)map();  // 创建一个空的map容器。map<int, string> m1;// 2)map(initializer_list<pair<K, V>> il); // 使用统一初始化列表。map<int, string> m2( { { 8,"冰冰" }, { 3,"西施" }, { 1,"幂幂" }, { 7,"金莲" }, { 5,"西瓜" } } );// map<int, string> m2={ { 8,"冰冰" }, { 3,"西施" }, { 1,"幂幂" }, { 7,"金莲" }, { 5,"西瓜" } };// map<int, string> m2   { { 8,"冰冰" }, { 3,"西施" }, { 1,"幂幂" }, { 7,"金莲" }, { 5,"西瓜" } };for (auto& val : m2)cout << val.first << "," << val.second << "  ";cout << endl;// 3)map(const map<K, V>&m);  // 拷贝构造函数。map<int, string> m3 = m2;for (auto& val : m3)cout << val.first << "," << val.second << "  ";cout << endl;// 4)map(Iterator first, Iterator last);  // 用迭代器创建map容器。auto first = m3.begin();  first++;auto last = m3.end();  last--;map<int, string> m4(first,last);for (auto& val : m4)cout << val.first << "," << val.second << "  ";cout << endl;// 5)map(map<K, V> && m);  // 移动构造函数(C++11标准)。
}

3.特性操作

size_t size() const;        // 返回容器的实际大小(已使用的空间)。
bool empty() const;      // 判断容器是否为空。
void clear();             // 清空容器。

4.元素操作

V &operator[](K key);             // 用给定的key访问元素。
const V &operator[](K key) const;  // 用给定的key访问元素,只读。
V &at(K key);                     // 用给定的key访问元素。
const V &at(K key) const;         // 用给定的key访问元素,只读。

注意:
1)[ ]运算符:如果指定键不存在,会向容器中添加新的键值对;如果指定键存在,则读取或修改容器中指定键的值。
2)at()成员函数:如果指定键不存在,不会向容器中添加新的键值对,而是直接抛出out_of_range 异常。

  • eg:
#include <iostream>
#include <map>
using  namespace std;int main()
{map<string, string> m( { { "08","冰冰" }, { "03","西施" }, { "01","幂幂" }, { "07","金莲" }, { "05","西瓜" } } );cout << "m[08]=" << m["08"] << endl;     // 显示key为08的元素的value。cout << "m[09]=" << m["09"] << endl;    // 显示key为09的元素的value。key为09的元素不存在,将添加新的键值对。m["07"] = "花花";                                          // 把key为07的元素的value修改为花花。m["12"] = "小乔";                                          // 将添加新的键值对。for (auto& val : m)cout << val.first << "," << val.second << "  ";cout << endl;
}

5.赋值操作

给已存在的容器赋值,将覆盖容器中原有的内容。

1)map<K,V> &operator=(const map<K,V>& m);         // 把容器m赋值给当前容器。
2)map<K,V> &operator=(initializer_list<pair<K,V>> il);  // 用统一初始化列表给当前容器赋值。

6.交换操作

交换的是树的根结点。

void swap(map<K,V>& m);    // 把当前容器与m交换。

7.比较操作

bool operator == (const map<K,V>& m) const;
bool operator != (const map<K,V>& m) const;

8.查找操作

1)查找键值为key的键值对
在map容器中查找键值为key的键值对,如果成功找到,则返回指向该键值对的迭代器;失败返回end()。

iterator find(const K &key);
const_iterator find(const K &key) const;  // 只读。

2)查找键值>=key的键值对
在map容器中查找第一个键值>=key的键值对,成功返回迭代器;失败返回end()。

iterator lower_bound(const K &key);
const_iterator lower_bound(const K &key) const;  // 只读。

3)查找键>key的键值对
在map容器中查找第一个键值>key的键值对,成功返回迭代器;失败返回end()。

iterator upper_bound(const K &key);
const_iterator upper_bound(const K &key) const;  // 只读。

4)统计键值对的个数
统计map容器中键值为key的键值对的个数。

size_t count(const K &key) const;
  • eg:
#include <iostream>
#include <map>
using  namespace std;int main()
{map<string, string> m( { { "08","冰冰" }, { "03","西施" }, { "01","幂幂" }, { "07","金莲" }, { "05","西瓜" } } );for (auto& val : m)cout << val.first << "," << val.second << "  ";cout << endl;// 在map容器中查找键值为key的键值对,如果成功找到,则返回指向该键值对的迭代器;失败返回end()。auto it1 = m.find("05");if (it1 != m.end())cout << "查找成功:" << it1->first << "," << it1->second << endl;elsecout << "查找失败。\n";// 在map容器中查找第一个键值 >= key的键值对,成功返回迭代器;失败返回end()。auto it2 = m.lower_bound("05");if (it2 != m.end())cout << "查找成功:" << it2->first << "," << it2->second << endl;elsecout << "查找失败。\n";//    在map容器中查找第一个键值 > key的键值对,成功返回迭代器;失败返回end()。auto it3 = m.upper_bound("05");if (it3 != m.end())cout << "查找成功:" << it3->first << "," << it3->second << endl;elsecout << "查找失败。\n";//   统计map容器中键值为key的键值对的个数。cout << "count(05)=" << m.count("05") << endl;   // 返回1。cout << "count(06)=" << m.count("06") << endl;   // 返回0。
}

9.插入和删除

1)void insert(initializer_list<pair<K,V>> il);  // 用统一初始化列表在容器中插入多个元素。
2)pair<iterator,bool> insert(const pair<K,V> &value);  // 在容器中插入一个元素,返回值pair:first是已插入元素的迭代器,second是插入结果。
3)void insert(iterator first,iterator last);  // 用迭代器插入一个区间的元素。
4)pair<iterator,bool> emplace (...);  // 将创建新键值对所需的数据作为参数直接传入,map容器将直接构造元素。返回值pair:first是已插入元素的迭代器,second是插入结果。
例:mm.emplace(piecewise_construct, forward_as_tuple(8), forward_as_tuple("冰冰", 18));
5)iterator emplace_hint (const_iterator pos,...); // 功能与第4)个函数相同,第一个参数提示插入位置,该参数只有参考意义,如果提示的位置是正确的,对性能有提升,如果提示的位置不正确,性能反而略有下降,但是,插入是否成功与该参数元关。该参数常用end()和begin()。成功返回新插入元素的迭代器;如果元素已经存在,则插入失败,返回现有元素的迭代器。
6)size_t erase(const K & key);  // 从容器中删除指定key的元素,返回已删除元素的个数。
7)iterator erase(iterator pos);  // 用迭代器删除元素,返回下一个有效的迭代器。
8)iterator erase(iterator first,iterator last);  // 用迭代器删除一个区间的元素,返回下一个有效的迭代器。
  • eg:
#include <iostream>
#include <map>
using  namespace std;class CGirl        // 超女类。
{public:string m_name;   // 超女姓名。int      m_age;       // 超女年龄。/*CGirl() : m_age(0) {cout << "默认构造函数。\n";}*/CGirl(const string name, const int age) : m_name(name), m_age(age) {cout << "两个参数的构造函数。\n";}CGirl(const CGirl & g) : m_name(g.m_name), m_age(g.m_age) {cout << "拷贝构造函数。\n";}
};int main()
{//map<int, CGirl> mm;//mm.insert     (pair<int, CGirl>(8, CGirl("冰冰", 18)));                // 一次构造函数,两次拷贝构造函数。//mm.insert     (make_pair<int, CGirl>(8, CGirl("冰冰", 18)));     // 一次构造函数,两次拷贝构造函数。//mm.emplace(pair<int, CGirl>(8, CGirl("冰冰", 18)));                // 一次构造函数,两次拷贝构造函数。//mm.emplace(make_pair<int, CGirl>(8, CGirl("冰冰", 18)));     // 一次构造函数,两次拷贝构造函数。//mm.emplace(8, CGirl("冰冰", 18));                                             // 一次构造函数,一次拷贝构造函数。//mm.emplace(8, "冰冰", 18);                                                        // 错误。//mm.emplace(piecewise_construct, forward_as_tuple(8), forward_as_tuple("冰冰", 18));  // 一次构造函数。//for (const auto& val : mm)// cout << val.first << "," << val.second.m_name << "," << val.second.m_name << "  ";//cout << endl;//return 0;map<int, string> m;// 1)void insert(initializer_list<pair<K,V>> il);  // 用统一初始化列表在容器中插入多个元素。m.insert({ { 8,"冰冰" }, { 3,"西施" }});m.insert({ pair<int,string>(1,"幂幂"), make_pair<int,string>(7,"金莲"), {5,"西瓜"}});m.insert({ { 18,"冰冰" }, { 3,"西施" } });// 2)pair<iterator,bool> insert(const pair<K,V> &value);  // 在容器中插入一个元素,返回值pair:first是已插入元素的迭代器,second是插入结果。auto ret = m.insert(pair<int, string>(18, "花花"));if (ret.second == true) cout << "插入成功:" << ret.first->first << "," << ret.first->second << endl;else cout << "插入失败。\n";// 3)void insert(iterator first, iterator last);  // 用迭代器插入一个区间的元素。// 4)pair<iterator, bool> emplace(...);  // 将创建新键值对所需的数据作为参数直接传入,map容器将直接构造元素。// 返回值pair:first是已插入元素的迭代器,second是插入结果。auto ret1 = m.emplace(20, "花花");if (ret1.second == true) cout << "插入成功:" << ret1.first->first << "," << ret1.first->second << endl;else cout << "插入失败。\n";// 5)iterator emplace_hint(const_iterator pos, ...); // 功能与第4)个函数相同,第一个参数提示插入位置,该参数只有参考意义,如果提示的位置是正确的,// 对性能有提升,如果提示的位置不正确,性能反而略有下降,但是,插入是否成功与该参数元关。// 该参数常用end()和begin()。成功返回新插入元素的迭代器;如果元素已经存在,则插入失败,返回现// 有元素的迭代器。m.emplace_hint(m.begin(), piecewise_construct, forward_as_tuple(23), forward_as_tuple("冰棒")); for (auto& val : m)cout << val.first << "," << val.second << "  ";cout << endl;
}

六、unordered_map容器

1.哈希表

哈希表长(桶的个数):数组的长度;
哈希函数:

size_t hash(const T& key){...//key%小于哈希表长的最大质数
}

装填因子:元素总数/表长,其值越大,效率越低

unordered_map容器封装了哈希表,查找、插入和删除元素时,只需要比较几次key的值。

包含头文件: #include<unordered_map>
unordered_map容器的元素是pair键值对。
unordered_map类模板的声明:
template <class K, class V, class _Hasher = hash<K>, class _Keyeq = equal_to<K>,class _Alloc = allocator<pair<const K, V>>>
class unordered_map : public _Hash<_Umap_traits<K, V, _Uhash_compare<K, _Hasher, _Keyeq>, _Alloc, false>>
{…
}创建std::unordered_map类模板的别名:
template<class K,class V>
using umap = std::unordered_map<K, V>;

第一个模板参数K:key的数据类型(pair.first)。
第二个模板参数V:value的数据类型(pair.second)。
第三个模板参数_Hasher:哈希函数,默认值为std::hash
第四个模板参数_Keyeq:比较函数,用于判断两个key是否相等,默认值是std::equal_to。
第五个模板参数_Alloc:分配器,缺省用new和delete。

2.构造函数

1)umap();  // 创建一个空的umap容器。
2)umap(size_t bucket);  // 创建一个空的umap容器,指定了桶的个数,下同。
3)umap(initializer_list<pair<K,V>> il); // 使用统一初始化列表。
4)umap(initializer_list<pair<K,V>> il, size_t bucket); // 使用统一初始化列表。
5)umap(Iterator first, Iterator last);  // 用迭代器创建umap容器。
6)umap(Iterator first, Iterator last, size_t bucket);  // 用迭代器创建umap容器。
7)umap(const umap<K,V>& m);  // 拷贝构造函数。
8)umap(umap<K,V>&& m);  // 移动构造函数(C++11标准)。
  • eg:
#include <iostream>
#include <unordered_map>
using  namespace std;template<class K, class V>
using umap = std::unordered_map<K, V>;   int main()
{// 1)umap();  // 创建一个空的map容器。umap<int, string> m1;// 2)umap(initializer_list<pair<K, V>> il); // 使用统一初始化列表。umap<int, string> m2({ { 8,"冰冰" }, { 3,"西施" }, { 1,"幂幂" }, { 7,"金莲" }, { 5,"西瓜" } });// umap<int, string> m2={ { 8,"冰冰" }, { 3,"西施" }, { 1,"幂幂" }, { 7,"金莲" }, { 5,"西瓜" } };// umap<int, string> m2   { { 8,"冰冰" }, { 3,"西施" }, { 1,"幂幂" }, { 7,"金莲" }, { 5,"西瓜" } };for (auto& val : m2)cout << val.first << "," << val.second << "  ";cout << endl;// 3)umap(const map<K, V>&m);  // 拷贝构造函数。umap<int, string> m3 = m2;for (auto& val : m3)cout << val.first << "," << val.second << "  ";cout << endl;// 4)umap(Iterator first, Iterator last);  // 用迭代器创建map容器。auto first = m3.begin();  first++;auto last = m3.end();  last--;umap<int, string> m4(first, last);for (auto& val : m4)cout << val.first << "," << val.second << "  ";cout << endl;// 5)umap(map<K, V> && m);  // 移动构造函数(C++11标准)。
}

3.特性操作

1)size_t size() const;        // 返回容器中元素的个数。
2)bool empty() const;      // 判断容器是否为空。
3)void clear();             // 清空容器。
4)size_t max_bucket_count();     // 返回容器底层最多可以使用多少桶,无意义。
5)size_t bucket_count();          // 返回容器桶的数量,空容器有8个桶。
6)float load_factor();   // 返回容器当前的装填因子,load_factor() = size() / bucket_count()。
7)float max_load_factor();        // 返回容器的最大装填因子,达到该值后,容器将扩充,缺省为1。
8)void max_load_factor (float z ); // 设置容器的最大装填因子。
9)iterator begin(size_t n);        // 返回第n个桶中第一个元素的迭代器。
10)iterator end(size_t n);          // 返回第n个桶中最后一个元素尾后的迭代器。
11)void reserve(size_t n);          // 将容器设置为至少n个桶。
12)void rehash(size_t n);           // 将桶的数量调整为>=n。如果n大于当前容器的桶数,该方法会将容器重新哈希;如果n的值小于当前容器的桶数,该方法可能没有任何作用。
13)size_t bucket_size(size_t n);     // 返回第n个桶中元素的个数,0 <= n < bucket_count()。
14)size_t bucket(K &key);          // 返回值为key的元素对应的桶的编号。

4.元素操作

V &operator[](K key);             // 用给定的key访问元素。
const V &operator[](K key) const;  // 用给定的key访问元素,只读。
V &at(K key);                     // 用给定的key访问元素。
const V &at(K key) const;         // 用给定的key访问元素,只读。

注意:

  • 1)[ ]运算符:如果指定键不存在,会向容器中添加新的键值对;如果指定键存在,则读取或修改容器中指定键的值。

  • 2)at()成员函数:如果指定键不存在,不会向容器中添加新的键值对,而是直接抛出out_of_range 异常。

  • eg:

#include <iostream>
#include <unordered_map>
using  namespace std;template<class K, class V>
using umap = std::unordered_map<K, V>;  int main()
{umap<string, string> m( { { "08","冰冰" }, { "03","西施" }, { "01","幂幂" }, { "07","金莲" }, { "05","西瓜" } } );cout << "m[08]=" << m["08"] << endl;     // 显示key为08的元素的value。cout << "m[09]=" << m["09"] << endl;    // 显示key为09的元素的value。key为09的元素不存在,将添加新的键值对。m["07"] = "花花";                                          // 把key为07的元素的value修改为花花。m["12"] = "小乔";                                          // 将添加新的键值对。for (auto& val : m)cout << val.first << "," << val.second << "  ";cout << endl;
}

5.赋值操作

给已存在的容器赋值,将覆盖容器中原有的内容。

1)umap<K,V> &operator=(const umap<K,V>& m);       // 把容器m赋值给当前容器。
2)umap<K,V> &operator=(initializer_list<pair<K,V>> il);  // 用统一初始化列表给容器赋值。

6.交换操作

交换的是树的根结点。

void swap(umap<K,V>& m);    // 把当前容器与m交换。

7.比较操作

bool operator == (const umap<K,V>& m) const;
bool operator != (const umap<K,V>& m) const;

8.查找操作

1)查找键值为key的键值对

  • 在umap容器中查找键值为key的键值对,如果成功找到,则返回指向该键值对的迭代器;失败返回end()。
iterator find(const K &key);
const_iterator find(const K &key) const;  // 只读。

2)统计键值对的个数

  • 统计umap容器中键值为key的键值对的个数。
size_t count(const K &key) const;

9.插入和删除

1)void insert(initializer_list<pair<K,V>> il);  // 用统一初始化列表在容器中插入多个元素。
2)pair<iterator,bool> insert(const pair<K,V> &value);  // 在容器中插入一个元素,返回值pair:first是已插入元素的迭代器,second是插入结果。
3)void insert(iterator first,iterator last);  // 用迭代器插入一个区间的元素。
4)pair<iterator,bool> emplace (...);  // 将创建新键值对所需的数据作为参数直接传入,map容器将直接构造元素。返回值pair:first是已插入元素的迭代器,second是插入结果。
例:mm.emplace(piecewise_construct, forward_as_tuple(8), forward_as_tuple("冰冰", 18));
5)iterator emplace_hint (const_iterator pos,...); // 功能与第4)个函数相同,第一个参数提示插入位置,该参数只有参考意义。对哈希容器来说,此函数意义不大。
6)size_t erase(const K & key);  // 从容器中删除指定key的元素,返回已删除元素的个数。
7)iterator erase(iterator pos);  // 用迭代器删除元素,返回下一个有效的迭代器。
8)iterator erase(iterator first,iterator last);  // 用迭代器删除一个区间的元素,返回下一个有效的迭代器。
  • eg:
#include <iostream>
#include <unordered_map>
using  namespace std;template<class K, class V>
using umap = std::unordered_map<K, V>;class CGirl        // 超女类。
{public:string m_name;   // 超女姓名。int      m_age;       // 超女年龄。/*CGirl() : m_age(0) {cout << "默认构造函数。\n";}*/CGirl(const string name, const int age) : m_name(name), m_age(age) {cout << "两个参数的构造函数。\n";}CGirl(const CGirl& g) : m_name(g.m_name), m_age(g.m_age) {cout << "拷贝构造函数。\n";}
};int main()
{//umap<int, CGirl> mm;mm.insert     (pair<int, CGirl>(8, CGirl("冰冰", 18)));                // 一次构造函数,两次拷贝构造函数。mm.insert     (make_pair<int, CGirl>(8, CGirl("冰冰", 18)));     // 一次构造函数,两次拷贝构造函数。mm.emplace(pair<int, CGirl>(8, CGirl("冰冰", 18)));                // 一次构造函数,两次拷贝构造函数。mm.emplace(make_pair<int, CGirl>(8, CGirl("冰冰", 18)));     // 一次构造函数,两次拷贝构造函数。mm.emplace(8, CGirl("冰冰", 18));                                             // 一次构造函数,一次拷贝构造函数。// mm.emplace(8, "冰冰", 18);                                                        // 错误。//mm.emplace(piecewise_construct, forward_as_tuple(8), forward_as_tuple("冰冰", 18));  // 一次构造函数。//for (const auto& val : mm)// cout << val.first << "," << val.second.m_name << "," << val.second.m_name << "  ";//cout << endl;//return 0;umap<int, string> m;// 1)void insert(initializer_list<pair<K,V>> il);  // 用统一初始化列表在容器中插入多个元素。m.insert({ { 8,"冰冰" }, { 3,"西施" } });m.insert({ pair<int,string>(1,"幂幂"), make_pair<int,string>(7,"金莲"), {5,"西瓜"} });m.insert({ { 18,"冰冰" }, { 3,"西施" } });// 2)pair<iterator,bool> insert(const pair<K,V> &value);  // 在容器中插入一个元素,返回值pair:first是已插入元素的迭代器,second是插入结果。auto ret = m.insert(pair<int, string>(18, "花花"));if (ret.second == true) cout << "插入成功:" << ret.first->first << "," << ret.first->second << endl;else cout << "插入失败。\n";// 3)void insert(iterator first, iterator last);  // 用迭代器插入一个区间的元素。// 4)pair<iterator, bool> emplace(...);  // 将创建新键值对所需的数据作为参数直接传入,umap容器将直接构造元素。// 返回值pair:first是已插入元素的迭代器,second是插入结果。auto ret1 = m.emplace(20, "花花");if (ret1.second == true) cout << "插入成功:" << ret1.first->first << "," << ret1.first->second << endl;else cout << "插入失败。\n";// 5)iterator emplace_hint(const_iterator pos, ...); m.emplace_hint(m.begin(), piecewise_construct, forward_as_tuple(23), forward_as_tuple("冰棒"));for (auto& val : m)cout << val.first << "," << val.second << "  ";cout << endl;
}

六、queue容器

queue容器的逻辑结构是队列,物理结构可以是数组或链表,主要用于多线程之间的数据共享。

包含头文件: #include<queue>
queue类模板的声明:
template <class T, class _Container = deque<T>>
class queue{……
}

第一个模板参数T:元素的数据类型。
第二个模板参数_Container:底层容器的类型,缺省是std::deque,可以用std::list,还可以用自定义的类模板。

  • queue容器不支持迭代器。

1.构造函数

1)queue();  // 创建一个空的队列。
2)queue(const queue<T>& q);  // 拷贝构造函数。
3)queue(queue<T>&& q);  // 移动构造函数(C++11标准)。
析构函数~queue()释放内存空间。

2.常用操作

1)void push(const T& value);  // 元素入队。
2)void emplace(…);           // 元素入队,…用于构造元素。C++11
3)size_t size() const;          // 返回队列中元素的个数。
4)bool empty() const;        // 判断队列是否为空。
5)T &front();                 // 返回队头元素。
6)const T &front();           // 返回队头元素,只读。
7)T &back();                 // 返回队尾元素。
8)const T &back();           // 返回队头元素,只读。
9)void pop();                // 出队,删除队头的元素。
  • eg:
#include <iostream>
#include <queue>
#include <deque>
#include <list>
using  namespace std;class girl       // 超女类。
{public:int m_bh;             // 编号。string m_name;  // 姓名。girl(const int& bh, const string& name) : m_bh(bh), m_name(name) {}
};int main()
{// template <class T, class _Container = deque<T>>// class queue {//   ……// }// 第一个模板参数T:元素的数据类型。// 第二个模板参数_Container:底层容器的类型,缺省是std::deque,可以用std::list,还可以用自定义的类模板。queue<girl, list<girl>> q;          // 物理结构为链表。//queue<girl, deque<girl>> q;    // 物理结构为数组。//queue<girl> q;                           // 物理结构为数组。//queue<girl, vector<girl>> q;    // 物理结构为vector,不可以。   q.push(girl(3, "西施"));   // 效率不高。q.emplace(8, "冰冰");     // 效率更高。q.push(girl(5, "幂幂"));q.push(girl(2, "西瓜"));while (q.empty() == false){cout << "编号:" << q.front().m_bh << ",姓名:" << q.front().m_name << endl;q.pop();}
}

3.其他操作

1)queue &operator=(const queue<T> &q);    // 赋值。
2)void swap(queue<T> &q);    // 交换。
3)bool operator == (const queue<T> & q) const; // 重载==操作符。
4)bool operator != (const queue<T> & q) const; // 重载!=操作符。

七、STL其他容器

1.array(静态数组)

1)物理结构

  • 在栈上分配内存(其余STL都是堆上的容器),创建数组的时候,数组长度必须是常量,创建后的数组大小不可变。
template<class T, size_t size>
class array{private:T elems_[size]; ……
};

2)迭代器

  • 随机访问迭代器。

3)特点

  • 部分场景中,比常规数组更方便(能用于模板),可以代替常规数组。

4)各种操作

1)void fill(const T & val);     // 给数组填充值(清零)。
2)size_t size();               // 返回数组的大小。
3)bool empty() const;        // 无意义。
4)T &operator[](size_t n);
5)const T &operator[](size_t n) const;  // 只读。
6)T &at(size_t n);
7)const T &at(size_t n) const;          // 只读。
8)T *data();            // 返回数组的首地址。
9)const T *data() const; // 返回数组的首地址。
10)T &front();          // 第一个元素。
11)const T &front();    // 第一个元素,只读。
12)const T &back();    // 最后一个元素,只读。
13)T &back();        // 最后一个元素。
  • eg:
#include <iostream>
#include <array>
using  namespace std;void func(int arr[][6],int len)
//void func(int (* arr)[6], int len)
//{//  for (int ii = 0; ii < len; ii++)
//  {//      for (int jj = 0; jj < 6; jj++)
//          cout << arr[ii][jj] << " ";
//      cout << endl;
//  }
//}//void func(const array < array<int, 5>, 10 >& arr)
//{//  for (int ii = 0; ii < arr.size(); ii++)
//  {//      for (int jj = 0; jj < arr[ii].size(); jj++)
//          cout << arr[ii][jj] << " ";
//      cout << endl;
//  }
//}//使用函数模板不用关心数组的长度
template <typename T>
void func(const T& arr)
{for (int ii = 0; ii < arr.size(); ii++){for (int jj = 0; jj < arr[ii].size(); jj++)cout << arr[ii][jj] << " ";cout << endl;}
}int main()
{//int aa[11] = {1,2,3,4,5,6,7,8,9,10,11};         // 一维数组。//array<int, 10> aa = { 1,2,3,4,5,6,7,8,9,10 };         // 一维数组。//for (int ii = 0; ii < 10; ii++)            // 传统的方法。//   cout << aa[ii] << " ";//cout << endl;////for (int ii = 0; ii < aa.size(); ii++)  // 利用array的size()方法。//   cout << aa[ii] << " ";//cout << endl;////for (auto it= aa.begin(); it < aa.end(); it++)      // 使用迭代器。//  cout << *it << " ";//cout << endl;//for (auto val : aa)                           // 基于范围的for循环。//  cout << val << " ";//cout << endl;//int bb[10][6];//for (int ii = 0; ii < 10; ii++)  // 对二维数组赋值。//{// for (int jj = 0; jj < 6; jj++)//      bb[ii][jj] = jj * 10 + ii;//}//func(bb,10);  // 把二维数组传给函数。array< array<int, 5>, 10 > bb;  // 二维数组,相当于int bb[10][5]。for (int ii = 0; ii < bb.size(); ii++)  // 对二维数组赋值。{for (int jj = 0; jj < bb[ii].size(); jj++)bb[ii][jj] = jj * 10 + ii;}func(bb);  // 把二维数组传给函数。
}

array在C++的用处:

  • std::array的好处主要在于函数的参数,C语言的数组会退化成指针,而C++则可以避免该问题(但是C++形参定义修改下,好像也能解决这个问题)

C Array版:

const int MAX = 10;
const int MAX2 = 9;//const int (&arr)[MAX]也可以解决这个问题.......
void fuck(const int arr[])
{for (size_t i = 0; i < MAX; i++) {shit(arr[i]);...}int bitch = std::accumulate(arr, arr + MAX, 0, crap);...
}int main()
{int arr1[MAX];int arr2[MAX2];int *ptr;fuck(arr1); // okfuck(arr2); // fuck!fuck(ptr);  // fuck!
}

std::arrary版本

const int MAX = 10;
const int MAX2 = 9;void fuck(const std::array<int, MAX> &arr)
{for (int a : arr) {shit(a);...}int bitch = std::accumulate(arr.begin(), arr.end(), 0, crap);...
}int main()
{std::array<int, MAX> arr1;std::array<int, MAX2> arr2;int *ptr;fuck(arr1); // okfuck(arr2); // won't compilefuck(ptr);  // won't compile
}
  • ref:总结:std::array的好处有三个:

2.deque(双端队列)

1)物理结构

  • deque容器存储数据的空间是多段等长的连续空间构成,各段空间之间并不一定是连续的。

  • 为了管理这些连续空间的分段,deque容器用一个数组存放着各分段的首地址。

  • 通过建立数组,deque容器的分段的连续空间能实现整体连续的效果。

  • 当deque容器在头部或尾部增加元素时,会申请一段新的连续空间,同时在数组中添加指向该空间的指针。

2)迭代器

  • 随机访问迭代器。

3)特点

  • 提高了在两端插入和删除元素的效率,扩展空间的时候,不需要拷贝以前的元素。
  • 在中间插入和删除元素的效率比vector更糟糕。
  • 随机访问的效率比vector容器略低。

4)各种操作

  • 与vector容器相同。

3.forward_list(单链表)

1)物理结构

  • 单链表。

2)迭代器

  • 正向迭代器。

3)特点

  • 比双链表少了一个指针,可节省一丢丢内存,减少了两次对指针的赋值操作。
  • 如果单链表能满足业务需求,建议使用单链表而不是双链表。

4)各种操作

  • 与list容器相同。

4.multimap

底层是红黑树。

  • multimap和map的区别在:multimap允许关键字重复,而map不允许重复。
    各种操作与map容器相同。

eg:C++ multimap查找相同键的键值对方法

1、使用find和count:
count(k) 求出键k的出现次数
  find(k) 返回第一个拥有键k的实例

multimap<int, int>::size_type  cnt = testMap.count(searchItem);
multimap<int, int>::iterator  iter = testMap.find(searchItem);
for(;cnt > 0; cnt--, iter++)
{cout<<iter->first<<" "<<iter->second<<endl;
}

2、使用lower_bound与upper_bound:

 lower_bound(k)返回迭代器指向不小于K的第一个元素upper_bound(k)返回迭代器指向 大于k的第一个元素multimap<int, int>::iterator iterBeg = testMap.lower_bound(searchItem);
multimap<int, int>::iterator iterEnd = testMap.upper_bound(searchItem);
for(;iterBeg != iterEnd;iterBeg++)
{cout<<iterBeg->first<<"->"<<iterBeg->second<<endl;
}

3、使用equal_range:
equal_range(k):函数的返回值是一个pair,分别存放相同键k的迭代器区间。

auto ret = testMap.equal_range(searchItem);
auto it = ret.first;
while(it!=ret.second)
{cout<<it->first<<"->"<<it->second<<endl;++it;
}

4.测试

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;int main()
{multimap<int, int> testMap;testMap.insert(make_pair(5,1));testMap.insert(make_pair(5,2));testMap.insert(make_pair(5,3));testMap.insert(make_pair(5,4));int searchItem = 5;/*第一种方法*/multimap<int, int>::size_type  cnt = testMap.count(searchItem);multimap<int, int>::iterator  iter = testMap.find(searchItem);for(;cnt > 0; cnt--, iter++){cout<<iter->first<<"->"<<iter->second<<endl;}cout<<endl;/*第二种方法*/multimap<int, int>::iterator iterBeg = testMap.lower_bound(searchItem);multimap<int, int>::iterator iterEnd = testMap.upper_bound(searchItem);for(;iterBeg != iterEnd;iterBeg++){cout<<iterBeg->first<<"->"<<iterBeg->second<<endl;   }cout<<endl;/*第三种方法*/auto ret = testMap.equal_range(searchItem);auto it = ret.first;while(it!=ret.second){cout<<it->first<<"->"<<it->second<<endl;++it;}return 0;
}

5.set&multiset

底层是红黑树。

  • set和map的区别在:map中存储的是键值对,而set只保存关键字。
  • multiset和set的区别在:multiset允许关键字重复,而set不允许重复。
    各种操作与map容器相同。

6.unordered_multimap

底层是哈希表。
unordered_multimap和unordered_map的区别在:unordered_multimap允许关键字重复,而unordered_map不允许重复。
各种操作与unordered_map容器相同。

7.unordered_set&unordered_multiset

底层是哈希表。
unordered_set和unordered_map的区别在:unordered_map中存储的是键值对,而unordered_set只保存关键字。
unordered_multiset和unordered_set的区别在:unordered_multiset允许关键字重复,而unordered_set不允许重复。
各种操作与unordered_map容器相同。

8.priority_queue(优先队列)

优先级队列相当于一个有权值的单向队列queue,在这个队列中,所有元素是按照优先级排列的。
底层容器可以用deque和list。
各种操作与queue容器相同。

9.stack(栈)

底层容器可以用deque和list。

10.std::hash

C++11 std::hash 用法小结

  • ref:从0基础系统化学习C++,不可能学不会,https://gitee.com/jiwangreal/code_bbs_code

C++11后的常用容器和迭代器相关推荐

  1. 几种常用容器的迭代器类型

    几种常用容器的迭代器类型 容器 迭代器类型 vector 随机存取 deque 随机存取 set 双向,元素为常量 multiset 双向,元素为常量 list 双向 map 双向,key为常量 mu ...

  2. 11、java常用单词(转载)

    abstract (关键字) 抽象 ['.bstr.kt] access vt.访问,存取 ['.kses]'(n.入口,使用权) algorithm n.算法 ['.lg.riem] annotat ...

  3. C++中STL和容器、迭代器、算法之间的关系

    自学习C++以来,一直对STL.容器.迭代器.算法甚是困惑. 参考一些资料,加上自己的理解,整理如下 如果说程序等于数据结构+算法,STL就是一个小程序库,之所以说小,是因为容器模板中常用的函数有限, ...

  4. C++primer第十一章 关联容器 11.1使用关联容器 11.2 关联容器概述

    关联容器和顺序容器有着根本的不同:关联容器中的元素是按关键字来保存和访问的.与之相对,顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的. 虽然关联容器的很多行为与顺序容器相同,但其不同之处反映 ...

  5. C++容器,迭代器,容器的适配器

    容器 顺序容器 主要靠下标和迭代器进行操作.顺序性的主要靠下标,链式的靠迭代器访问. 包含了顺序型的容器和链式的容器. 连续型的包括: vector:向量,可以快速扩展和删除元素,在队尾的操作有优势! ...

  6. C++认识容器的迭代器

    1.错误的map删除操作 假设有个map容器,用于存储大学班级中各个家乡省份对应的学生数,key为省份中文全拼,value为学生数.现需要删除人数为0的记录,删除代码如下: map<string ...

  7. 深入解析C++ STL中的常用容器

    转载:http://blog.csdn.net/u013443618/article/details/49964299 这里我们不涉及容器的基本操作之类,只是要讨论一下各个容器其各自的特点.STL中的 ...

  8. 【Docker学习笔记 三】Docker常用容器安装及图形化管理工具

    上一篇Blog详细介绍了如何在CentOS上进行Docker的安装.卸载以及如何进行镜像加速,了解了Docker大致的运行流程以及常用的命令.时隔半个月之后,度过了过节失落期后再次拾起来自己的年度计划 ...

  9. Re 从零开始的C++之路(四)容器和迭代器

    前面一节了解了字符串,今天学习容器以及迭代器的相关操作.容器和字符串一样,都属于C++标准库里的标准库类型.形如其名,容器可以理解为专门用来存储数据的容器状的数据结构.是指定类型的数据的集合,并且可以 ...

最新文章

  1. 每日一篇——lodash—array——differenceBy
  2. 果蝇大脑研究能够改进计算机相似性搜索算法
  3. Linux 内核中断体系 初探
  4. WEB开发者应该有哪些必备的技能?
  5. java课程 数独 文库_数独java
  6. 14.ZooKeeper Java API 使用样例
  7. grep命令---Linux学习笔记
  8. C++面试常见问题整理汇总(面试者必看哦!)
  9. 计算机系统制造时间成本,如何构建计算机成本核算系统.doc
  10. 第2讲 | 区块链到底是怎么运行的?
  11. 创建Maven web工程不能解析EL表达式的解决办法
  12. Rails中select2 实现多选框的效果
  13. 网络游戏植入营销案例
  14. 基带单元(BBU)与无线单元(RRU)之间的高速链路-CPRI接口
  15. codesmith mysql 注释_代码生成工具:CodeSmith 安装、改造适配Mysql 字段注释、DLL修改 及批量生成实体类代码...
  16. Spring Cloud微服务实战
  17. LTP(Linux Test Project)使用指南
  18. 引擎选择:GameMaker
  19. 苹果退款_苹果 App Store 里自动订阅续费的应用可以退款吗?
  20. lipo otool 动态库、静态库的生成

热门文章

  1. 84---Python 雨滴模拟
  2. 自己开发了一个财神小童子合成
  3. js实现图片滚动以及点击切换效果
  4. 如何利用HTML5做游戏营销
  5. matplotlib中的label标签
  6. 华为nova7pro的计算机,为什么说华为nova7pro在华为nova6面前只能算个“弟弟”?
  7. 快直播视频搬运无人直播技术原理及分析
  8. 通信值勤维护管理条例_加强固定通信台站值勤管理与建设的探讨
  9. (转)负载大逃亡:四十二路怪兽联军及七条逃生法则
  10. 有多少人知道微信来电铃声可以跟更换?