STL

STL是提高C++编写效率的利器。

#include <vector>

vector是一个变长数组,用多少开多少,支持随机访问,不支持在任意位置O(1)插入。为了保证效率,元素的增删一般应该在末尾进行。vector支持比较,比较按照字典序进行。

声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <vector> //头文件

using namespace std;

int main()
{
vector<int> a; //定义动态int数组a
vector<int> b[233]; //定义二维int数组,第一维是静态的,长233,第二维是动态的

struct Rec
{
int x, y;
};

vector<Rec> c; //自定义的结构体类型也可以保存为vector

return 0;
}

size/empty

a.size()函数返回vector的实际长度(包含的元素个数),a.empty()函数返回一个bool类型,判断vector是否为空,若为空则返回True,否则返回False。二者的时间复杂度都是O(1)

1
2
3
vector<int> a;
a.size();
a.empty();

所有的STL容器都支持这两个方法,含义也相同。

clear

a.clear()函数用于清空vector里的元素。

1
2
vector<int> a;
a.clear();

迭代器

迭代器就像STL容器的指针,可以用*操作符解除引用。

一个保存int的vector的迭代器声明方法为:

1
vector<int>::iterator it = a.begin();

vector的迭代器是随机访问迭代器,可以把vector的迭代器与一个整数相加减,其行为和指针的移动类似。可以把vector的两个迭代器相减,其结果也和指针相减类似,得到两个迭代器对应下标之间的距离。

begin/end

begin/end相当于两个特殊的迭代器。

begin函数返回指向vector中第一个元素的迭代器。例如a是一个非空的vector,则*a.begin()a[0]的作用相同。

1
2
a.begin() == a[0];
a.end() == a[n]; //错误用法

所有的容器都可以视作一个前闭后开的结构,end函数返回vector的尾部,即第n 个元素再往后的“边界”*a.end()a[n]都是越界访问,其中n = a.size()

vector的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <vector>

using namespace std;

int main()
{
vector<int> a({1, 2, 3});

cout << a[0] << ' ' << *a.begin() << endl;
cout << a[1] << ' ' << *(a.begin() + 1) << endl;
cout << a[2] << ' ' << *(a.begin() + 2) << endl;

for (int i = 0; i < 3; i ++)
cout << a[i] << ' ';

return 0;
}

输出

1
2
3
4
1 1
2 2
3 3
1 2 3

vector的遍历

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
#include <iostream>
#include <vector>

using namespace std;

int main()
{
vector<int> a({1, 2, 3});

for (int i = 0; i < 3; i ++) //类似数组遍历
cout << a[i] << ' ';
cout << endl;

for (vector<int>::iterator i = a.begin(); i != a.end(); i ++) //利用迭代器遍历
cout << *i << ' ';
cout << endl;

for (auto i = a.begin(); i < a.end(); i ++) //使用auto自动判断
cout << *i << ' ';
cout << endl;

for (auto x : a) //范围遍历,类似string
cout << x << ' ';
cout << endl;

return 0;
}

输出

1
2
3
4
1 2 3 
1 2 3
1 2 3
1 2 3

front/back

front函数返回vector的第一个元素,等价于*a.begin()a[0]

back函数返回vector的最后一个元素,等价于*--a.end()a[a.size() – 1]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <vector>

using namespace std;

int main()
{
vector<int> a({1, 2, 3});

cout << a.front() << ' ' << *a.begin() << ' ' << a[0] << endl;
cout << a.back() << ' ' << *(a.end() - 1) << ' ' << a[a.size() - 1] << endl;

return 0;
}

输出

1
2
1 1 1
3 3 3

push_back()pop_back()

a.push_back(x)把元素x插入到vector a的尾部。时间复杂度为O(1)。

b.pop_back()删除vector b的最后一个元素。时间复杂度为O(1)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <vector>

using namespace std;

int main()
{
vector<int> a({1, 2, 3});

a.push_back(4);

for (auto x : a)
cout << x << ' ';
cout << endl;

a.pop_back();

for (auto y : a)
cout << y << ' ';
cout << endl;

return 0;
}

输出

1
2
1 2 3 4 
1 2 3

#include <queue>——队列

头文件<queue>主要包括循环队列queue和优先队列priority_queue两个容器。

声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <queue>

queue<int> q; //队列声明
queue<double> a;

struct Rec
{
int a, x, y;
};
queue<Rec> b;

priority_queue<int> c; //优先队列,默认大根堆
priority_queue<int,vector<int>, greater<int>> d; //小根堆
priority_queue<pair<int, int>> //`pair`双关键字二元组

sturct Rec
{
int a, b;
bool operator < (const Rec& t) const
{
return a < t.a;
}
};
priority_queue<int> p; //结构体类型的大根堆优先队列,结构体中必须定义小于号,反之需要小于号

循环队列queue

只能从队尾插入,只能从队头弹出。先进先出

1
2
3
4
q.push(1);              // 从队尾插入
q.pop(); // 从队头弹出
q.front(); // 返回队头元素
q.back(); // 返回队尾元素

优先队列priority_queue

1
2
3
q.push(1);              //插入一个元素
q.top(); //取出最大值
q.pop(); //删除最大值

※ 队列、优先队列、栈没有clear()函数,其他的STL容器都有。想要清空队列,可以重新定义一次q = queue<int>()

#include <stack>——栈

先进后出。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <stack>

using namespace std;

int main()
{
stack<int> stk;

stk.push(1); //向栈顶插入元素
stk.top(); //返回栈顶元素
stk.pop(); //删除栈顶元素
}

#include <deque>——双端队列

双端队列deque是一个支持在两端高效插入或删除元素的连续线性存储空间。它就像是vectorqueue的结合。与vector相比,deque在头部增删元素仅需要 O(1)的时间;与queue相比,deque像数组一样支持随机访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <deque>
#include <iostream>

using namespace std;

int main()
{
deque<int> a;
a.begin();
a.end();
a.front();
a.back();

a.push_back(1); //在最后插入元素
a.push_front(1); //在开头插入元素

a[0]; //随机访问元素

a.pop_back(); //弹出最后一个元素
a.pop_front(); //弹出开头一个元素

a.clear(); //清空双端队列
}

#include <set>

头文件<set>主要包括setmultiset两个容器,分别是“有序集合”和“有序多重集合”,即前者的元素不能重复,而后者可以包含若干个相等的元素。setmultiset的内部实现是一棵红黑树,它们支持的函数基本相同。

声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <set>
#include <iostream>

using namespace std;

int main()
{
set<int> a; //不能包含重复元素
multiset<int> b; //可以包含重复元素

struct Rec
{
int x, y;
bool operator< (const Rec& t) const
{
return x < t.x;
}
};

set<Rec> c;

multiset<double> s;
}

size/empty/clear

vector类似。

迭代器

setmultiset的迭代器称为“双向访问迭代器”,不支持“随机访问”,支持*解除引用,仅支持++--两个与算术相关的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <set>
#include <iostream>

using namespace std;

int main()
{
set<int> a; //不能包含重复元素

set<int>::iterator it = a.begin();
it ++; //表示有序序列的下一个元素
it --; //表示有序序列的上一个元素
++ it;
-- it;
}

begin/end

返回集合的首/尾迭代器,时间复杂度均为O(1)。

s.begin()是指向集合中最小元素的迭代器。

s.end()是指向集合中最大元素的下一个位置的迭代器,--s.end()是指向集合中最大元素的迭代器。

insert

s.insert()是把一个元素x插入到集合s中,时间复杂度为O(logn)。

set中,若元素已存在,则不会重复插入该元素,对集合的状态无影响。

find

s.find(x)在集合s中查找等于x的元素,并返回指向该元素的迭代器。若不存在则返回s.end(),时间复杂度为O(logn)。

lower_bound/upper_bound

s.lower_bound(x)查找大于等于x的元素中最小的一个,并返回指向该元素的迭代器。

s.upper_bound(x)查找大于x的元素中最小的一个,并返回指向该元素的迭代器。

时间复杂度为O(logn)。

erase

it是一个迭代器,s.erase(it)表示从s中删除迭代器it指向的元素,时间复杂度为O(logn)。

x是一个元素,s.erase(x)表示从s中删除所有等于x的元素,时间复杂度为 O(k+logn),k是被删除的元素个数。

count

s.count()返回集合s中等于x的元素个数,时间复杂度为 O(k+logn),k是元素x的个数。

如果sset即元素不允许重复的有序集合,则若s中存在元素xs.count()返回1,否则返回0。

unordered_set/unordered_multiset

相当于无序的set/multiset,不能进行二分操作即lower_bound/upper_bound,但是其他操作的时间复杂度都是O(1),相比set/multiset效率更高。

#include <map>

map容器是一个键值对key-value的映射,其内部实现是一棵以key为关键码的红黑树。mapkeyvalue可以是任意类型,其中key必须定义小于号运算符。

声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <map>
#include <vector>

using namespace std;

int main()
{
map<int, int> a; //定义map

a[1] = 2; //插入元素<1, 2>

a[100000000] = 3;
cout << a[100000000] << endl;

map<string, int> b;
b["Koku"] = 2;

map<string, vector<int>> c;
c["Kuro"] = vector<int>({1, 2, 3});
}

size/empty/clear/begin/end

均与set类似。

insert/erase

set类似,但其参数均是pair<key_type, value_type>

find

h.find(x)在变量名为hmap中查找keyx的二元组。

[]操作符

h[key]返回key映射的value的引用,时间复杂度为O(logn)。

可以很方便地通过h[key]来得到key对应的value,还可以对h[key]进行赋值操作,改变key对应的value

unordered_map

效率更高。(C++ 11支持)

#include <bitset>

进行位运算,是一个二进制的串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <bitset>

using namespace std;

int main()
{
bitset<1000> a; //定义了一个长度为1000的二进制串
a[0] = 1;

a.set(3); //把a[3]设成1
a.reset(3); //把a[3]设成0

a &= b;
a != b; //位运算

a.count(); //返回a中1的个数
}

pair

pair定义一个二元组,元素可以是任意类型。

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

using namespace std;

int main()
{
pair<int, string> a;
a = {3, "ditf"};

a.first();
a.second();
}

pair支持双关键字比较,第一个关键字相同则比较第二个。

位运算与常用库函数

位运算

符号 运算
&
~
^ 异或
>> 右移
<< 左移
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

using namespace std;

int main()
{
int a = 3, b = 6;

cout << (a ^ b) << endl;
//异或^按位运算,3的二进制是011,6的二进制是111,所以结果是101,十进制是5

return 0;
}

常用操作:

  1. x的第k位数字 x >> k & 1
    例:输出13的二进制的每一位
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>

    using namespace std;

    int main()
    {
    int a = 13; //13的二进制表示是1101
    for (int i = 3; i >= 0; i --)
    cout << (a >> i & 1) << ' ';

    return 0;
    }
    输出
    1
    1 1 0 1
  2. lowbit(x) = x & -x.返回x的最后一位1。若a = 11010则返回01,若a = 11000则返回1000。用补码求负数,-x就是x取反后加1。

常用库函数

reverse翻转

时间复杂度为O(n)。

翻转一个vector:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main()
{
vector<int> a({1, 2, 3, 4, 5});

reverse(a.begin(), a.end());

for (auto x : a)
cout << x << ' ';

return 0;
}

输出

1
5 4 3 2 1

翻转一个数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <algorithm>

using namespace std;

int main()
{
int a[] = {1, 2, 3, 4, 5};
reverse(a, a + 5); //类似vector的左开右闭,第二个参数是数组a[]中的最后一个元素的下一个地址

for (auto x : a)
cout << x << ' ';

return 0;
}

输出

1
5 4 3 2 1

unique去重

返回去重(只去掉相邻的相同元素)之后的尾迭代器(或指针),仍然为前闭后开,即这个迭代器是去重之后末尾元素的下一个位置。

1 1 2 3 3 4 -unique-> 1 2 3 4

返回值是新数组的下一个位置,即a.end()

该函数常用于离散化,利用迭代器(或指针)的减法,可计算出去重后的元素个数。

将一个数组去重:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main()
{
int a[] = {1, 1, 2, 2, 3, 3, 4};

int m = unique(a, a + 7) - a;
cout << m << endl;

for (int i = 0; i < m; i ++)
cout << a[i] << ' ';

return 0;
}

输出

1
2
4
1 2 3 4

将一个vector去重:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main()
{
vector<int> a({1, 1, 2, 2, 3, 3, 4});

//int m = unique(a.begin(), a.end()) - a.begin();

//先将vector<int> a中的元素去重并存放到a的前几个元素中,再用erase删除后面重复的元素,这样a中只剩下不重复的元素
a.erase(unique(a.begin(), a.end()), a.end());

for (auto x : a)
cout << x << ' ';

return 0;
}

输出

1
1 2 3 4

random_shuffle随机打乱

用法与reverse相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <algorithm>
#include <vector>
#include <ctime>

using namespace std;

int main()
{
vector<int> a({1, 2, 3, 4, 5});

srand(time(0)); //保证每次随机生成的顺序都不同

random_shuffle(a.begin(), a.end());

for (auto x : a)
cout << x << ' ';

return 0;
}

输出

1
5 2 4 3 1 

sort排序

对两个迭代器(或指针)指定的部分进行快速排序。可以在第三个参数传入定义大小比较的函数,或者重载“小于号”运算符。

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
#include <iostream>
#include <algorithm>
#include <vector>
#include <ctime>

using namespace std;

bool cmp(int a, int b) //判断a是否应该排在b的前面
{
return a > b; //若a > b,则a在b前面
}

int main()
{
vector<int> a({1, 2, 3, 4, 5});

srand(time(0)); //保证每次随机生成的顺序都不同

random_shuffle(a.begin(), a.end());
for (auto x : a)
cout << x << ' ';
cout << endl;

sort(a.begin(), a.end()); //默认从小到大排序
for (auto x : a)
cout << x << ' ';
cout << endl;

sort(a.begin(), a.end(), greater<int>()); //从大到小排序
for (auto x : a)
cout << x << ' ';
cout << endl;

sort(a.begin(), a.end(), cmp); //使用cmp参数,若a比b大则a排在前面
for (auto x : a)
cout << x << ' ';

return 0;
}

输出

1
2
3
4
3 2 1 4 5
1 2 3 4 5
5 4 3 2 1
5 4 3 2 1

sort排序结构体

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
//法一:定义比较函数
#include <iostream>
#include <algorithm>
#include <vector>
#include <ctime>

using namespace std;

struct Rec
{
int x, y;
}a[5];

bool cmp(Rec a, Rec b) //判断a是否应该排在b的前面
{
return a.x < b.x; //若a > b,则a在b前面
}

int main()
{


for (int i = 0; i < 5; i ++ )
{
a[i].x = -i;
a[i].y = i;
}

for (int i = 0; i < 5; i ++ )
printf("(%d, %d)",a[i].x, a[i].y);
cout << endl;

sort(a, a + 5, cmp);

for (int i = 0; i < 5; i ++ )
printf("(%d, %d)",a[i].x, a[i].y);
cout << endl;

return 0;
}
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
//法二:在结构体中重载小于号
#include <iostream>
#include <algorithm>
#include <vector>
#include <ctime>

using namespace std;

struct Rec
{
int x, y;
bool operator< (const Rec &t) const
{
return x < t.x;
}
}a[5];

int main()
{


for (int i = 0; i < 5; i ++ )
{
a[i].x = -i;
a[i].y = i;
}

for (int i = 0; i < 5; i ++ )
printf("(%d, %d)",a[i].x, a[i].y);
cout << endl;

sort(a, a + 5);

for (int i = 0; i < 5; i ++ )
printf("(%d, %d)",a[i].x, a[i].y);
cout << endl;

return 0;
}

输出

1
2
(0, 0)(-1, 1)(-2, 2)(-3, 3)(-4, 4)
(-4, 4)(-3, 3)(-2, 2)(-1, 1)(0, 0)

lower_bound/upper_bound二分

lower_bound的第三个参数传入一个元素x,在两个迭代器(指针)指定的部分上执行二分查找,返回指向第一个大于等于x的元素的位置的迭代器(指针)。

upper_bound的用法和lower_bound大致相同,唯一的区别是查找第一个大于x的元素。

两个迭代器(指针)指定的部分需要提前排好序。

数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <algorithm>

using namespace std;

int main()
{
int a[] = {1, 2, 4, 5, 6};

int *p = lower_bound(a, a + 5, 4); //返回大于等于4的数组元素的指针
int *q = upper_bound(a, a + 5, 4); //返回大于4的数组元素的指针

cout << *p << ' ' << *q << endl;

return 0;
}

输出

1
4 5

vector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
vector<int> a({1, 2, 4, 5, 6});

int t = lower_bound(a.begin(), a.end(), 2) - a.begin();
int m = upper_bound(a.begin(), a.end(), 2) - a.begin();

cout << a[t] << ' ' << a[m] << endl;

return 0;
}

输出

1
2 4