stl map 查找不到返回什么_C++中常用的STL容器总结一

YOU CAN DRINK ALL YOU LIKE, BUT IN THE MORNING YOU GET HEADACHE WITH THE SAME PROBLEMS.


0x71 C++STL(1)

本节介绍STL中vector、queue、priority_queue、deque、set、multiset、map、bitset八种算法竞赛中比较有用的容器。

头文件vector

vector可理解为变长数组,它的内部实现基于倍增的思想。按照下列思路可以大致实现一个vector:设n、m为vector的实际长度和最大长度。向vector加入元素前,若n=m,则在内存中申请2m的连续空间,并把内容转移到新的地址上,同时释放旧的空间,再执行插入。从vector中删除元素后,若n<=m/4,则释放一半的空间。

vector支持随机访问,即对于任意的下标0<=i

声明

#include
vector<int> a;
vector<int> a[233]; //相当于第一维长度233,第二维长度动态变化的int数组
struct rec{};
vector c; //自定义的结构体也可以保存在vector数组中

size/empty

时间复杂度为O(1),所有的容器都支持这两种方法,含义也都相同,之后就不再重复给出。

clear

清空vector

迭代器

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

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

vector<int>::iterator it;

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

begin/end

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

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

for (vector<int>::iterator it =  a.begin(); it != a.end(); i++) {
    cout <endl;
}

front/back

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

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

push_back/pop_back()

a.push_back(x) 把元素x插入到vector a的尾部。

a.pop_back() 删除vector 的最后一个元素。

用vector代替邻接表保存有向图

const int MAX_EDGES = 100010;
vector<int> ver[MAX_EDGES], edge[MAX_EDGES];

// 保存从x到y权值为z的有向边
void add(int x, int y, int z) {
    ver[x].push_back(y);
    edge[x].push_back(z);
}

// 遍历从x出发的所有边
for (int i = 0; i     int y = ver[x][i], z = edge[x][i];
    // 有向边(x, y, z)
}

头文件queue

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

声明

queue<int> q;
struct rec{}; queue q;
priority_queue<int> q;
priority_queueint, int>> q;

循环队列queue

方法描述示例时间复杂度
push入队(从队尾)a.push(element)O(1)
pop出队(从队头)a.pop()O(1)
front队头元素int x = q.front();O(1)
back队尾元素int y = q.back()lO(1)

优先队列priority_queue

priority_queue可理解为一个大根堆。

方法描述示例时间复杂度
push把元素插入堆q.push(x);O(log n)
pop删除堆顶元素q.pop();O(log n)
top查询堆顶元素(最大值)int x = q.top();O(1)

重载“

priority_queue中存储的元素类型必须定义小于号,较大的元素会被放在堆顶。内置的int,string等基本类型本身就可以比较大小,若用自定义的结构体类型,则需要重载“

例如下面的poi结构体保存了二维平面上点的坐标和编号,比较大小时,先比较横坐标,再比较纵坐标,并且考虑了精度误差:

struct poi{int id; double x, y;};
const int eps = 1e-8;

bool operator const poi &a, const poi &b) {
    return a.x + eps }

priority_queue实现小根堆

priority_queue实现小根二叉堆的方式一般有两种。

对于int等内置数值类型,可以把要插入的元素的相反数放入堆中。等从堆中取出元素时,再把它取相反数变回原来的元素。这样就相当于把小的放在堆顶。

例如要插入1、2、3三个数时,改为插入-1、-2、-3.此时-1最大,处于堆顶,把-1取出后再变回-(-1),相当于取出了最小的1。

更为通用的解决方案是,建立自定义结构体类型,重载小于号,但是当做大于号来编写函数,例如:

struct rec {int id; double value; };

bool operator const rec &a, const rec &b) {
    return a.value > b.value;
}

这样priority_queue会认为大的更小,小的更大,从而实现大根堆时,value较小的rec元素会被放在堆顶。

懒惰删除法

STL的优先队列不支持删除堆中任意元素,这给我们带来了许多不便。

懒惰删除法(又称延迟删除法)就是一种应对策略。当遇到删除操作时,仅在优先队列之外做一些特殊的记录(例如记录元素的最新值),用于辨别那些堆中尚未清除的过时元素。当从堆顶取出最值时,再检查最值元素是不是过时的,若是,则重新取出下一个最值。换言之,元素的删除被延迟到堆顶进行。

头文件deque

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

方法描述示例时间复杂度
[]随机访问与vector类似O(1)
begin/enddeque的头/尾迭代器与vector迭代器类似O(1)
front/back对头/队尾元素与queue类似O(1)
push_back从队尾入队q.push_back(x);O(1)
push_front从队头入队q.push_front(y);O(1)
pop_front从对头出队q.pop_front();O(1)
pop_back从队尾出队q.pop_back();O(1)
clear清空队列q.clear();O(n)

set头文件

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

声明

set<int> s;
struct rec{...}; set s;multiset<double> s;

与优先队列一样,set和multiset存储的元素必须定义小于号运算符。

size/empty/clear

与vector类似,分别为元素个数、是否为空、清空。前两者的时间复杂度为O(1)。

迭代器

set和multiset的迭代器称为双向访问迭代器,不支持随机访问,支持星号*解除引用,仅支持++和--两个与运算相关的操作。

设it是一个迭代器,例如:

set<int>::iterator it;

若把it++,则it会指向下一个元素。这里的下一个是指在元素从小到大排序的结果中,排在it下一名的元素。同理,若把it--,则it将会指向排在上一个的元素。

请注意,执行++和--操作的时间复杂度都是O(log n)。执行操作前后,务必仔细检查,避免迭代器指向的位置超出首、尾迭代器之间的范围。

begin/end

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

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

s.end()是指向集合中最大元素的下一个位置的迭代器,换而言之,就像vector一样,是一个前闭后开的形式,因此--s.end()是指向集合中最大元素的迭代器。

insert

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

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

下面的代码把n个整数插入有序多重集合multiset,并从小到大输出,时间复杂度为O(nlog n),相当于进行了一次排序。假设n个整数目前存储在数组a[1~n]中。

multiset<int> s;
for (int i = 1; i <= n; i++) s.insert(a[i]);
for (multiset<int>::iterator it = s.begin(); it != s.end(); it++) {
    cout <endl;
}

find

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

lower_bound/upper_bound

这两个函数的用法与find类似,但查找的条件有所不同,时间复杂度为O(log n)。

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

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

erase

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

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

如果想从multiset中删掉至多一个等于x的元素,可以执行:

if ((it = s.find()) != s.end()) s.erase(it);

count

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

d6bee468fe074ad14d9750546b8696ab.png


d45fe39c99eb6cef2083db9fa224706f.png
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值