vector

  • 可变长集合
  • 根据源码,vector自身完全杜绝浅拷贝
  • 初始化:
1
2
3
4
5
6
vector<T> v1;	// 定义空vector,执行默认初始化
vector<T> v2(v1); // 深拷贝
vector<T> v2 = v1; // 深拷贝
vector<T> v3(n, val); // 初始化,包含n个val
vector<T> v4{a, b, c}; // 初始化
vector<T> v5 = {a, b, c}; //同上
  • 操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
vec.push_back(val);
vec.pop_back();
vec.empty();
vec.size(); // 当前已有元素个数 vec.resize(n); // 改变队列长度
vec.max_size(); //当前最大容量 vec.reserve(n); // 改变容量
vec.clear();
vec.front();
vec.back();
vec.begin(); // 返迭代器
vec.end(); // 返迭代器
vector<T>::reverse_iterator ritor = vec.rbegin(); // 返回反向迭代器,倒序后,指向首元素
vector<T>::reverse_iterator r_itor_2 = vec.rend(); // 返回反向迭代器,倒序后,指向尾元素后一位
vec[n]; // 返引用,不可用下标形式添加新元素
v1 == v2; // 元素数量相同,对应位置元素值相同, true
// < > <= >= // 比较大小
vec.erase(pos); // 删除,pos为迭代器 返回指向下一元素的迭代器
vec.erase(pos_1, pos_2); // 删除, [) 返回指向下一元素的迭代器
vec.insert(pos, val); // 插入,pos为迭代器,在pos之前插,返新插入元素的迭代器
vec.insert(pos, n, val); // 插n个val,返回首个新插入元素的迭代器
vec.insert(pos, beg, end); // 在pos前插[beg, end)内的元素,返回首个新插入元素的迭代器

for (auto &i : vec) {...} // 范围for循环访问修改(修改用&)

vec_2.assign(vec.begin(), vec.end()); // 清除vec_2容器内容,将vec区间[)内的元素赋值给vec_2
vec_2.assign(n, val); // 清除vec_2容器内容,将n个val赋给vec_2

swap(vec_1, vec_2); v1.swap(v2); // 替换,效果相同

string

  • 可变长字符序列
  • 初始化:
1
2
3
4
string s1;	// 默认初始化,s1是一个空字符串
string s2 = s1;
string s3 = "2333";
string s4(10, 'c');
  • 操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
s.empty();
s.size(); // string无'\0'
s[n]; // 返引用,不可用下标形式添加新元素
s1 + s2;
s.append("2333"); // 末尾追加
s.replace(11, 3, "123456"); // 从s的下标11开始,删除3个字符,并在此处插入字符串"123456"。(replace = erase + insert)
s1 == s2; s1 != s2; // < <= > >=
s1.compare(s2); // 比较大小,s2可为string,可为char*。返回0,正数,负数
to_string(val); // 数值val转string
stoi(str); // string转int


for (auto &c : s) {...} // 范围for循环访问修改(修改用&)
  • 单个字符操作:
1
2
3
4
5
6
7
8
9
10
11
#include<cctype>

isalnum(c); // c是否为字母或数字
isalpha(c); // c是否为字母
isdigit(c); // c是否为数字
islower(c); // c是否小写字母
isupper(c); // c是否大写字母
ispunct(c); // c是否为标点符号
isxdigit(c); // c是否十六进制数字
tolower(c); // 输出小写字符或原字符
toupper(c); // 输出大写字符或原字符
  • 构造string的其他方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const char *cp = "Hello World!!!";	// 包含空字符, 计算长度strlen(cp)
char noNULL[] = { 'H', 'i', 'w', 'k' }; // 不包含空字符, 计算长度sizeof(noNULL) / sizeof(noNULL[0])
string str("233333333");

string s1(cp); // 拷贝cp中字符直至遇到空字符,即全拷贝
string s2(cp, n); // 拷贝cp前n个字符,若n > strlen(cp),则s2多出来的部分是'\0'占位

string s3(noNULL); // 未定义,因noNULL不是以空字符结束
string s4(noNULL, n); // 拷贝noNULL中前n个字符,若n > (sizeof(noNULL) / sizeof(noNULL[0])),则s4多出来的部分是不可控字符!

string s(str, pos); // 从下标pos拷贝str到s,若pos > str.size()报错未定义。(pos == str.size()时,拷贝结果为"")
string s(str, pos, len); //从下标pos拷贝len个字符给s,若pos > str.size()报错未定义,len大了没关系,拷到末尾自动结束

str.substr(pos, n); // 从str下标pos开始,拷贝n个字符,返回一个string。若pos > str.size()报错out_of_range。n大了,没关系,拷贝到末尾自动结束。不指定n则默认从下标pos拷贝到末尾


// string类型支持顺序容器的赋值运算符assign、insert、erase操作。除此之外,string还定义了额外的insert和erase版本

// 除了接收迭代器的insert和erase版本外,string还提供了接受下标的版本
s.insert(s.size(), 5, '!'); // 在s末尾插入5个!
s.erase(s.size() - 5, 5); // 从s删除最后5个字符

// string还提供了接受C风格字符数组的insert和assign版本。
const char *cp = "Stately, plump Buck";
s.assign(cp, 7); // s == "Stately" 赋予s的是从cp指向的地址开始的7个字符
s.insert(s.size(), cp + 7); // s == "Stately, plump Buck"

// 也可以指定将来自其他string或子字符串的字符插入到当前string中
string s = "some string", s2 = "some other string";
s.insert(0, s2);
s.insert(0, s2, 0, s2.size());


// find搜索操作(大小写敏感) 匹配不到,返回string::npos
string name("AnnaBelle");
auto pos1 = name.find("Anna"); // pos1 == 0 find全匹配,返回首次匹配的下标

string numbers("0123456789"), name("r2d2");
auto pos = name.find_first_of(numbers); // pos == 1 find_first_of匹配任意一个字符,返回首次匹配的下标
auto pos2 = name.find_first_of(numbers, 2); // pos2 == 3 name从下标2开始进行匹配
// 对应还有 find_last_of find_first_not_of(首次不匹配的下标) find_last_not_of

list

参考博文

  • 环形双向链表
  • 但不能循环访问,头节点和尾节点之间有一个end()存在。
1
2
3
4
5
list<int> test_cycle = { 1,2,3,4,5 };	// 1 -> 2 -> 3 -> 4 -> 5 -> end() ---- (1) -> (2) -> ...
auto itor = test_cycle.end();
itor = ++itor; // 报错,越不过去的。所以没办法循环访问
int res = *itor;
return 0;
  • 构造函数(顺序容器基本都差不多):
1
2
3
4
5
list<T> c0;	// 空链表
list<T> c1(3); // 建一个链表,含3个默认值为0的元素
list<T> c2(5, 2); //建一个链表,含5个值为2的元素
list<T> c4(c2); // 深拷贝
list<T> c5(c1.begin(), c1.end());
  • 成员函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
c.begin();
c.end();
c.rbegin();
c.rend();
c.assign(n, num);
c.assign(beg, end);
c.front(); // 返回首个插入链表的元素
c.back(); // 返回最后插入链表的元素
c.empty();
c.size();
c.max_size();
c.clear();
c.insert(pos, num); // pos为迭代器 返回新插入元素的迭代器
c.insert(pos, n, num); // 返回首个新插入元素的迭代器
c.insert(pos, beg, end); // 返回首个新插入元素的迭代器
c.erase(pos); // pos为迭代器 返回下一个元素的迭代器
c.push_back(num);
c.pop_back();
c.push_front(num);
c.pop_front();
c.resize(n); // 重新定义链表的长度,超出原始长度部分用0代替,小于原始部分删除
c.resize(n, num); // 重新定义链表的长度,超出原始长度部分用num代替
c.swap(c2); swap(c1, c2);

c1.merge(c2); // 合并2个有序链表并使之有序,重新放到c1里,释放c2
c1.merge(c2, comp); // 同上,自定义比较规则
bool comp(int first, int second) {
return first < second;
}
list<int> list1{ 1, 3, 5 };
list<int> list2{ 2, 4, 6 };
list1.merge(list2, comp); // 1 2 3 4 5 6

c1.splice(c1.beg, c2); // 将c2连接在c1的beg位置,释放c2
c1.splice(c1.beg, c2, c2.beg); // 将c2的beg位置的元素(单个元素)连接到c1的beg位置,并在c2中释放掉beg位置的元素(单个元素)
c1.splice(c1.beg, c2, c2.beg, c2.end);

c1.remove(val); // 删除c1中值为val的元素
c1.remove_if(comp); // 删除满足条件的元素
bool comp(int val) {
return val > 5;
}
list<int> list1{ 1, 3, 5, 7, 9 };
list1.remove_if(comp); // 1 3 5

c1.reverse(); // 反转链表
c1.unique(); // 删除相邻的重复元素 1,2,2,2,3,4,3,5 -> 1,2,3,4,3,5

c.sort(); // 排序,默认升序
c.sort(comp); // 条件排序
bool comp(int val1, int val2) {
return n1 > n2;
}
c.sort(comp);

// 重载运算符
// operator== operator!= operator< operator<= operator> operator>=

forward_list

  • 单向链表,头文件 #include<forward_list>
  • 成员函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
f.front();	// 返回首个插入链表的元素
// f.back(); 不支持操作
// f.size(); 不支持操作
f.begin();
f.end();
f.cbegin();
f.cend();
// f.rbegin(); 不支持操作
// f.rend(); 不支持操作
f.push_front(val); // 在链表首元素前,插入元素
// f.push_back(val); 不支持操作
f.insert_after(itor, val); // 在itor指向元素之后插入val (一般insert都是之前插入)
// f.insert(itor, val); 不支持操作
f.insert_after(pos_itor, temp_itor1, temp_itor2); // 在f的pos_itor后面,插入temp的[temp_itor1, temp_itor2)元素
f.pop_front();
// f.pop_back(); 不支持操作
f.clear();
f.erase_after(itor); // 删除itor指向元素之后的元素 注意end报错
// f.erase(itor); 不支持操作
f.erase_after(itor1, itor2); // 删除(itor1, itor2)范围内元素,注意 左开又开

deque

参考博文

  • vector是单向开口连续线性空间;deque是双向开口连续线性空间,允许常数时间内对头部插删。

  • deque迭代器并非普通指针,其复杂度高于vector。除非必要,否则应尽量使用vector。(deque的空间由一段一段的定量连续空间构成,deque采用一块map作为主控,这个map是一块连续空间,其中每个元素都是指针,指向另一段较大的连续空间,称为缓冲区。 参考博文

  • 对deque进行排序,应先复制到vector,排序,再复制回deque。

  • 构造函数(顺序容器基本都差不多):

1
2
3
4
5
deque<T> a;
deque<T> a(10);
deque<T> a(10, 1);
deque<T> b(a);
deque<T> b(beg, end);
  • 操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
deq[n];
deq.front(); // 首元素
deq.back(); // 尾元素
deq.size();
deq.max_size();
deq.resize();
deq.empty();
deq.push_front(val);
deq.push_back(val);
deq.insert(pos, val); // pos为迭代器 返回新插入元素的迭代器
deq.insert(pos, n, val); // 返回首个新插入元素的迭代器
deq.insert(pos, pos1, pos2); // 返回首个新插入元素的迭代器
deq.pop_front();
deq.pop_back();
deq.erase(pos); // pos为迭代器 返回下一个元素的迭代器
deq.erase(pos1, pos2); // 返回下一个元素的迭代器
deq.clear();
deq.assign(n, val);
deq.swap(deq2);
deq.begin();
deq.end();
deq.rbegin();
deq.rend();
  1. 顺序容器适配器:stack,queue,priority_queue。

  2. 适配器是一种机制,能使某种事物的行为看起来像另一种事物。

  3. 默认情况下stack和queue是基于deque实现的,priority_queue是基于vector实现的。

  4. 所有适配器都要求容器具有添加和删除元素的能力,因此适配器不能构造在array上。所有适配器都要求容器具有访问尾元素的能力,因此适配器不能构造在forward_list上。

  5. stack可使用除array和forward_list外的任何容器进行构造;queue可构造在list和deque上,但不能构造在vector上;priority_deque需有随机访问的能力,因此可构造在vector和deque上,但不能构造在list上。

  6. 适配器虽构造于底层容器,但只能使用适配器本身的操作,不能使用底层容器的操作。

stack

  • 栈,后进先出,无遍历行为,无迭代器,单口
  • 常用操作:
1
2
3
4
5
6
7
8
9
stack<T> s;
stack<T> s(c); // 初始化s时,拷贝容器c内的元素
stack<T, vector<T>> str_stk; // 手动指定,使用vector做stack的底层
s.empty();
s.pop();
s.size();
s.push(val);
s.top(); // 返回栈顶,但不弹出
swap(a, b); a.swap(b);

queue

  • 队列,先进先出,无遍历行为,无迭代器,双口

  • 常用操作:

1
2
3
4
5
6
7
queue<T> q;
q.push(val);
q.pop(); // 弹出首个元素
q.front();
q.back();
q.empty();
q.size();

priority_queue

  • 优先级队列,自动排序,最高优先级的排在最前面(默认最大值)。多个元素具有相同优先级时,先来的排在前面。
  • 无遍历行为,无迭代器
  • 构造:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
priority_queue<T> pri_q;
priority_queue<int, vector<int>, greater<int>> pri_q; // 与其他容器不一样,排序为1 2 3 4 5
priority_queue<int, vector<int>, less<int>> pri_q; // 与其他容器不一样,排序为5 4 3 2 1

struct node {
int x;
bool operator<(const node& a) const {
return x < a.x;
}
};
priority_queue<node> pri_q; // node为结构体,需要定义<比较运算符

struct compare {
bool operator()(const ListNode* l, const ListNode* r) {
return l->val > r->val;
}
};
priority_queue<ListNode*, vector<ListNode*>, compare> pri_q;
  • 操作:
1
2
3
4
5
6
pri_q.size();
pri_q.empty();
pri_q.push(val);
pri_q.pop();
pri_q.top();
pri_q.back();

heap

  • 非STL基本容器,一般由vector构成,待后续整理

关联式容器

map、set、multimap(关键字可重复)、multiset(关键字可重复)、unordered_map(哈希函数实现)、unordered_set(哈希函数实现)、unordered_multimap(哈希函数实现,关键字可重复)、unordered_multiset(哈希函数实现,关键字可重复)

  • set、map可用于去重

  • 关联容器没有头尾,只有最大最小值。因此不存在push_back()、push_front()、pop_back()、pop_front()

  • 关联容器的迭代器是双向迭代器,非随机访问迭代器。

  • map迭代器解引用是一个pair;set迭代器解引用是元素值

  • set:获取某元素的迭代器后,只能读不能写,即不可修改set中的元素值。

  • map:按键排序,元素为pair,键不允许重复。不可修改键,只能修改值。

  • 使用迭代器遍历set、multiset、map、multimap时,迭代器默认按关键字升序遍历元素

    有关各容器排序实现,待整理重载后,继续整理!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main() {
vector<int> res_less; // 最终结果 1 2 3 4 5,与priority_queue相反
map<int, char, less<int>> my_map; // 默认就是less<int>
my_map.insert({ 1, 'a' });
my_map.insert({ 2, 'b' });
my_map.insert({ 3, 'c' });
my_map.insert({ 4, 'd' });
my_map.insert({ 5, 'e' });
map<int, char, less<int>>::const_iterator citor = my_map.cbegin();
while (citor != my_map.cend()) {
res_less.push_back(citor->first);
++citor;
}
return 0;
}

// map<int, char, greater<int>> my_map; // 最终结果为 5 4 3 2 1
  • map元素获取
    • pair:  .first    .second
    • map迭代器:  ->first    ->second
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
map<string, int> my_map;
my_map.insert({ "1", 1 }); // 返回一个pair。若插入成功(插入一个新元素),pair.first为插入元素的pair,pair.second为true;若插入失败(key重复,保持原有键值对,不更新),pair.first为原有键值对的pair,pair.second为false
my_map.insert({ "2", 2 });
my_map.insert({ "3", 3 });
my_map.insert({ "4", 4 });
my_map.insert({ "5", 5 });

map<string, int>::iterator itor = ++my_map.begin(); // 迭代器指向一个pair元素,pair的first成员保存const的key,pair的second成员保存value
string res1 = itor->first; // itor-> 解引用此迭代器并获取对应的成员变量
// itor->first = "2333"; // 错误!因pair的first成员const,即键值key不可修改
// itor->second = 2333; // 正确,key不可修改,但value可以
int res2 = itor->second;

pair<string, int> res3 = *itor;
string res4 = (*itor).first; // 即 itor->first 等价于 (*itor).first
int res5 = (*itor).second;

return 0;
1
2
3
4
5
6
7
8
// map insert插入返回值演示
map<int, string> myMap;
myMap.insert(make_pair(1, "111"));
pair<map<int, string>::iterator, bool> res = myMap.insert(make_pair(1, "222"));
bool ret = res.second; // false
int key = res.first->first; // 1
string value = res.first->second; // "111"
return 0;
  • 对于容器中不存在的元素,map、unordered_map下标操作可将该元素添加到容器;vector等容器将报错异常。

    因下标运算符可能插入一个新元素,因此只能对非const的map使用下标操作。

    set类型不支持下标操作,无意义。

    不能对multimap、unordered_multimap进行下标操作,存在重复值。

1
2
c[k];	// 若k不在c内,插入c
c.at(k); // 若k不在c内,抛out_of_range异常 (即便这是关联容器)
  • 与list相同,增删元素后原迭代器仍有效。
  • find查找元素:应使用关联容器自己的find方法(iter2 = iset.find(3)😉(结果若不存在,返尾后迭代器.end()),这会比STL提供的find方法快得多**(iter1 = find(iset.begin(), iset.end(), 3)😉**,因为STL提供的find方法是简单的循环搜索。
  • 初始化
1
2
3
4
5
6
set<string> exclude = {"the", "but", "and", "2333"};
map<string, int> word_count;
map<string, string> another = {{"Joyce", "James"}, {"Austen", "Jane"}, {"Dickens", "Charles"}};

set<int> iset(ivec.cbegin(), ivec.cend()); // iset自动去重
multiset<int> miset(ivec.cbegin() ivec.cend()); //multiset内包含重复元素
  • 对于有序容器map、multimap、set、multiset,关键字类型必须定义元素比较方法,标准库使用关键字<运算符

  • pair:标准库类型,头文件utility

    初始化

1
2
3
4
5
6
pair<T1, T2> p;
pair<T1, T2> p(val_1, val_2);
pair<T1, T2> p = {val_1, val_2};
make_pair(val_1, val_2);
return pair<string, int> (); // 隐式构造一个空pair并返回它
return {"2333", 123}; // 返回一个pair
  • 添加插入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// insert有两个版本,分别接受一对迭代器,或是一个初始化器列表
// 自动去重,重复元素仅保留首个元素,插入重复元素对容器无任何影响(不会更新原元素!)
vector<int> ivec = {2, 4, 6, 8, 2, 4, 6, 8};
set<int> set2;
set2.insert(ivec.cbegin(), ivec.cend()); // 返回值void
set2.insert({1, 3, 5, 7, 1, 3, 5, 7});

map<string, int> word_count;
word_count.insert({word, 1});
word_count.insert(make_pair(word, 1));
word_count.insert(pair<string, int>(word, 1));

// 对于map和set,只有当元素的关键字不在c中时才插入(构造)元素。函数返回一个pair,包含一个迭代器(指向具有指定关键字的元素)和一个bool值(指示是否插入成功)
// 插入成功时,迭代器指向新插入元素的位置,bool返true
// 插入失败时(插入重复元素),迭代器指向已存在元素的位置,bool返false
// 对于multi_map、multi_set,总会插入(或构造)给定元素,因此仅返回一个指向新元素位置的迭代器
c.insert(v);
c.emplace(args);
  • 删除元素

    提供3个erase版本,前两个和顺序容器相同,使用迭代器;关联容器提供一个额外的erase版本,使用关键字。

1
2
3
c.erase(p);	// 单个删除,p为迭代器,返回下一个元素的迭代器
c.erase(b ,e); // 范围删除,b、e为迭代器,返回下一个元素的迭代器
c.erase(k); // 从c中删除所有关键字为k的元素,返回实际删除元素的数量
  • 查找访问元素
1
2
3
4
5
6
7
8
9
10
11
12
set<int> iset = {0, 1, 2, 3, 4, 5};
iset.find(1); // 返迭代器,指向第一个关键字为k的元素。不存在返尾迭代器
iset.count(1); // 返元素个数

// lower_bound和upper_bound不可用于无序容器
// 下标和at操作只适用于非const的map和unordered_map
c.lower_bound(k); // 返迭代器,指向第一个关键字不小于k的元素 [
c.upper_bound(k); // 返迭代器,指向第一个关键字大于k的元素 )
c.equal_range(k); // 返迭代器pair,上面两者结合 [) 若k不存在,返回的两个迭代器均为c.end()

// 如果一个multimap或multiset中有多个元素具有相同关键字,则这些元素在容器中相邻存储
// 所以若想遍历multimap或multiset中某关键字的各元素,方法一:使用equal_range; 方法二:使用count + find
  • 无序容器不使用比较运算符组织容器,而是使用哈希函数和关键字类型的==运算符。

    无序容器的性能依赖于哈希函数的质量桶的数量和大小

    无序容器允许将不同关键字的元素映射到相同的桶。

杂货

  • emplace与insert的区别

    emplace直接调用构造函数,直接在容器中构造一个元素;insert先构造一个对象,然后调用拷贝构造函数,将该对象拷贝到容器。因此emplace少了一步拷贝操作,效率高一些,推荐使用。

    vector:insert(emplace),push_back(emplace_back)

    set:insert(emplace)

    map:insert(emplace)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<string>
#include<vector>
#include<iostream>
using namespace std;

struct Gift
{
Gift() : name(""), price(0) {}
Gift(string name, int price) :name(name), price(price) {}
Gift(const Gift &gift) :name(gift.name), price(gift.price) {}
~Gift() {}
bool operator<(const Gift &gift) const {
return price < gift.price;
}

string name;
int price;
};

int main() {
vector<Gift> myVec;
myVec.reserve(20); // 预先分配空间
myVec.push_back(Gift("C++", 50));
myVec.emplace_back("Java", 40);
myVec.insert(myVec.begin() + 1, Gift("C#", 30));
myVec.emplace(myVec.begin() + 1, "Python", 20);
vector<Gift>::const_iterator itor = myVec.begin();
for (; itor != myVec.end(); ++itor) {
cout << (*itor).name << endl;
}
return 0;
}
  • reverse函数

    求容器空间[first,last)的逆序,左闭右开,无返回值。first与last为迭代器。