STL容器
STL容器
vector
vector是变长数组,支持随机访问,不支持在任意位置O(1)插入
vector定义
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> a; //定义一个vector数组
vector<int> b[10]; //相当于第一维长10,第二位长度动态变化的int数组
struct res
{
int x,y;
};
vector<res> c; //自定义的类型也可放在vector中
return 0;
}
size函数
:返回vector的实际长度(包含的元素个数),时间复杂度为O(1)
empty函数
:返回一个bool类型,表明vector是否为空,时间复杂度为O(1)
clear函数
:把vector元素清空。
迭代器就像STL容器的“指针”,可以用星号“*”操作符
解除引用。
一个保存int的vector的迭代器声明方法为 vector<int>::iterator it;
vector的迭代器是“随机访问迭代器”,可以把vector的迭代器与一个整数相加减,其行为和指针的移动类似。可以把vector的两个迭代器相减,其结果也和指针相减类似,得到两个迭代器对应下标之间的距离
所有的容器都可以视作一个“前闭后开”
的结构.
begin函数
:返回指向vector中第一个元素的迭代器。例如a是一个非空的vector,则*a.begin()
与a[0]
的作用相同。
end函数
:返回vector的尾部,即第n个元素再往后的“边界”。*a.end()
与a[n]
都是越界访问,其中n=a.size()
。
vector的遍历
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> a({1,2,3,4,5}); //定义一个vector数组
for(int i=0;i<a.size();i++) cout << a[i] << ' ';
cout << endl;
for(vector<int>::iterator i = a.begin();i!=a.end();i++) cout << *i << ' '; // 迭代器遍历 迭代器可以用auto简写
cout << endl;
for(int x : a) cout << x << ' ';
cout << endl;
return 0;
}
vector 常用函数
front函数
:返回vector的第一个元素,等价于a.begin() 和 a[0]
back函数
:返回vector的最后一个元素,等价于==a.end() 和 a[a.size() – 1]
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> a({1,2,3,4,5}); //定义一个vector数组
cout << a.front() << ' ' << a[0] << ' ' << *a.begin() << endl;
cout << a.back() << ' ' << a[a.size()-1] << endl;
return 0;
}
push_back(x)
:把元素x插入到vector 的尾部。
pop_back()
删除vector 的最后一个元素。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> a({1,2,3,4,5}); //定义一个vector数组
for(auto x: a) cout << x << ' ';
cout << endl;
a.push_back(6); //末尾添加元素6
for(auto x: a) cout << x << ' ';
cout << endl;
a.pop_back(); //删除末尾元素
a.pop_back();
for(auto x: a) cout << x << ' ';
cout << endl;
return 0;
}
queue
头文件queue主要包括循环队列queue和优先队列priority_queue两个容器。
queue定义
#include <iostream>
#include <queue>
using namespace std;
int main()
{
queue<int> a; //定义队列
queue<int> b;
struct Rec
{
int x,y,z;
};
queue<Rec> c;
priority_queue<int> d; //大根堆
priority_queue<int,vector<int>,greater<int>> e; //小根堆
priority_queue<pair<int,int>> f; //二元组pair也可存于队列中
return 0;
}
注意:
自己定义的结构体存于优先队列,需要重载比较方法
大根堆需要重载小于号
小根堆需要重载大于号
#include <iostream>
#include <queue>
using namespace std;
int main()
{
struct Rec
{
int x,y;
bool operator< (const Rec& t) const //重载小于号
{
return x < t.x;
}
};
priority_queue<Rec> a;
a.push({1,2});
struct Rec2
{
int x,y;
bool operator> (const Rec2 &t) const //重载大于号
{
return x > t.x;
}
};
priority_queue<Rec2,vector<Rec2>,greater<Rec2>> b;
b.push({3,4});
return 0;
}
queue遍历
queue常用函数
普通队列:
push(x)
从队尾插入x
pop()
从队头弹出
frontx()
返回队头元素
back()
返回队尾元素
#include <iostream>
#include <queue>
using namespace std;
int main()
{
queue<int> q;
q.push(1); //队尾插入元素
q.push(2);
q.push(3);
cout << q.front() << endl; //队首元素
cout << q.back() << endl; //队尾元素
q.pop(); //从队首删除一个元素
cout << q.front() << endl; //队首元素
cout << q.back() << endl; //队尾元素
return 0;
}
priority_queue优先队列
push(x)
:向队列中插入元素x
pop()
: 删除优先队列中最大值
top
:返回优先队列的最大值
#include <iostream>
#include <queue>
using namespace std;
int main()
{
priority_queue<int> q; //定义大根堆
q.push(3);
q.push(1); //插入元素
q.push(2);
cout << q.top() << endl; //查看最大值
q.pop(); //弹出最大值
cout << q.top() << endl; //查看最大值
return 0;
}
注:queue,priority_queue,stack没有clear()
函数
清空操作(重新定义):
q = queue<int>();
stack
栈的定义与操作
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> stk; //定义栈
stk.push(1); //添加元素
stk.push(2);
stk.push(3);
cout << stk.top() << endl; //返回栈顶元素
stk.pop(); //删除栈顶元素
cout << stk.top() << endl;
return 0;
}
双端队列
双端队列deque是一个支持在两端高效插入或删除元素的连续线性存储空间。它就像是vector和queue的结合。与vector相比,deque在头部增删元素仅需要O(1)的时间;与queue相比,deque像数组一样支持随机访问。
双端队列的定义和操作
[] 随机访问
begin/end
:返回deque的头/尾迭代器
front/back
:队头/队尾元素
push_back
: 从队尾入队
push_front
:从队头入队
pop_back
:从队尾出队
pop_front
:从队头出队
clear
: 清空队列
#include <iostream>
#include <deque>
using namespace std;
int main()
{
deque<int> a;
a.begin(),a.end(); //返回deque的头/尾迭代器
a.front(),a.back(); //返回队头/队尾元素
a.push_back(1),a.push_front(2); //从队尾插入元素,从队首插入元素
a.pop_back(),a.pop_front(); //从队尾删除元素,从队首删除元素
a.clear() //清空元素
return 0;
}
Set
头文件set主要包括set和multiset两个容器,分别是“有序集合”和“有序多重集合”,即前者的元素不能重复,而后者可以包含若干个相等的元素。set和multiset的内部实现是一棵红黑树,它们支持的函数基本相同。
set定义
注意:当set集合中的元素为结构体时,该结构体必须实现运算符‘<’的重载
#include <iostream>
#include <set>
using namespace std;
const int N = 1e5+10;
int cnt[N],a[N];
int main()
{
set<int> a; //set的定义
struct Rec
{
int x,y;
bool operator< (const Rec &t) const //需要重载小于运算符
{
return x<t.x;
}
};
set<Rec> b;
return 0;
}
set的常用操作
clear()
删除set容器中的所有的元素
empty()
判断set容器是否为空
size()
返回当前set容器中的元素个数
set和multiset的迭代器称为“双向访问迭代器”,不支持“随机访问”,支持星号(*)解除引用,仅支持”++”和–“两个与算术相关的操作。
set<int>::iterator it;
若把it++,则it会指向“下一个”元素。这里的“下一个”元素是指在元素从小到大排序的结果中,排在it下一名的元素。同理,若把it–,则it将会指向排在“上一个”的元素。
insert(x)
把一个元素x插入到集合set中,时间复杂度O(logn)。
find(x)
在集合s中查找等于x的元素,并返回指向该元素的迭代器。若不存在,则返回s.end()。
lower_bound
,upper_bound
这两个函数的用法与find类似,但查找的条件略有不同,时间复杂度为 O(logn)。
lower_bound(x)
查找大于等于
x的元素中最小的一个,并返回指向该元素的迭代器
upper_bound(x)
查找大于
x的元素中最小的一个,并返回指向该元素的迭代器
erase(x)
若x是一个迭代器,则erase(x)
是指从s中删除迭代器x指向的元素,时间复杂度为O(logn)
若x是一个元素,erase(x)
是指从s中删除所有等于x的元素,时间复杂度为O(k+logn),其中k是被删除的元素个数。
count(x)
返回集合s中等于x的元素个数,时间复杂度为 O(k +logn),其中k为元素x的个数。
unordered_set
与set类似,但效率更高,但因为无序,所以不支持二分,没有upper_bound
和lower_bound
函数
unordered_multiset
在unordered_set 基础上允许存重复元素
unordered_set<int> a; //哈希表 不能存储重复元素
unordered_multiset<int> b; //哈希表 能存储重复元素
map
map的定义
自动建立key - value的对应。key 和 value可以是任意你需要的类型,包括自定义类型。
//第一种 insert方法
map.insert({key,value});
// 第二种 用"array"方式插入
map[key] = value;
#include <iostream>
#include <map>
#include <vector>
using namespace std;
int main()
{
map<string,int> a; //定义map
map<string,vector<int>> b;
a["yxc"] = 1; //类似于数组操作
vector<int> c({1,2,3});
b.insert({"acwing",c}); //insert方法
return 0;
}
map的常用操作
size()
,empty()
,clear()
,begin()
,end()
均与set类似。
find(x)
在map中查找key为x的二元组。
[]操作符
map[key] 返回key映射的value的引用,时间复杂度为O(logn)。
[]操作符可以很方便地通过key来得到key对应的value,还可以对map[key]进行赋值操作,改变key对应的value。
unodered_map
与map类似,区别在于无序,因此操作的复杂度更低,效率更高
unodered_map <int> a;
pair
pair是将2个数据组合成一组数据,两个值可以分别用pair.first和pair.second访问。
比较运算,先比较第一个元素,按照字典序,然后再比较第二个元素
#include <iostream>
using namespace std;
int main()
{
pair<string,int> a;
pair<string,int> b;
a = {"acwing",1};
b = make_pair("yxc",2);
cout << a.first << ' ' << a.second << endl;
cout << b.first << ' ' << b.second << endl;
return 0;
}
位运算与常用库函数
位运算
&
与
|
或
~
非
^
异或
>>
右移
<<
左移
常用操作:
(1) 求x的第k位数字
x >> k & 1
(2)返回x的最后一位1
返回x的二进制表达式中最低位的1所对应的值
lowbit(x) = x & -x
常用库函数
(1) reverse
翻转
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
vector<int> a({1,2,3,4,5});
reverse(a.begin(),a.end()); //翻转vector
for(int x : a) cout << x << ' ';
cout << endl;
int b[] = {1,2,3,4,5};
reverse(b,b+5); //翻转数组
for(int x : b) cout << x << ' ';
cout << endl;
return 0;
}
(2) unique
去重
返回去重之后的尾迭代器(或指针),仍然为前闭后开,即这个迭代器是去重之后末尾元素的下一个位置。该函数常用于离散化,利用迭代器(或指针)的减法,可计算出去重后的元素个数。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int a[] = {1,2,2,3,4,4,5};
vector<int> b({1,2,2,3,4,4,5});
int m = unique(a,a+7) - a; //计算去重后元素还有几个
cout << m << endl;
for(int i=0;i<m;i++) cout << a[i] << ' '; //输出去重后的数组
cout << endl;
b.erase(unique(b.begin(),b.end()),b.end()); //去重并删除后面重复的元素
for(int x : b) cout << x << ' ';
return 0;
}
(3) random_shuffle
随机打乱
用法与reverse相同
#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(int x:a) cout << x << ' ';
cout << endl;
return 0;
}
(4) sort
对两个迭代器(或指针)指定的部分进行快速排序。可以在第三个参数传入定义大小比较的函数,或者重载“小于号”运算符。
数组的排序:
从小到大:
sort(a.begin(),a.end()); //从小到大排序
从大到小:
sort(a.begin(),a.end(),greater<int>()); //从大到小排序
#include <iostream>
#include <algorithm>
#include <vector>
#include <ctime>
using namespace std;
int main()
{
vector<int> a({3,1,4,5,2});
sort(a.begin(),a.end()); //从小到大排序
for(int x:a) cout << x << ' ';
cout << endl;
sort(a.begin(),a.end(),greater<int>()); //从大到小排序
for(int x:a) cout << x << ' ';
cout << endl;
return 0;
}
结构体自定义排序
方法一:自定义比较函数cmp
#include <iostream>
#include <algorithm>
using namespace std;
struct Rec
{
int x,y;
}a[5];
bool cmp(Rec a,Rec b) //a是否应该排在b的前面
{
return a.x<b.y;
}
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;
}
方法二:在结构体内重载比较运算符
#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;
}
(5)lower_bound/upper_bound 二分(在排好序的基础上)
lower_bound
的第三个参数传入一个元素x,在两个迭代器(指针)指定的部分上执行二分查找,返回指向第一个大于等于
x的元素的位置的迭代器(指针)。
upper_bound
的用法和lower_bound大致相同,唯一的区别是查找第一个大于
x的元素。当然,两个迭代器(指针)指定的部分应该是提前排好序的。
#include <iostream>
#include <algorithm>
#include <vector>
#include <ctime>
using namespace std;
int main()
{
int a[] = {1,2,3,4,5,6};
int *p = lower_bound(a,a+6,3); //返回大于等于3的位置的迭代器
cout << *p << endl;
int *q = upper_bound(a,a+6,3); //返回大于3的位置的迭代器
cout << *q << endl;
return 0;
}