deque
和vector差不多,可以在前端后端插入,一般用deque取代vector,vector只能在后端插入push_back()。deque还可以push_front(),但是deque后端插入比vecto稍慢。
list:双向链表,不能使用下标,和数组vector deque不同。只能使用迭代器,来指示元素。push_front,push_back,insert(位置,值),位置一般用迭代器来指定位置,其中insert返回的是一个迭代器。例如lis.insert(a.begin, 12);返回的是一个迭代器。删除使用erase(位置);或者erase(重哪里a,到哪里b);其中a位置包括,而b位置是不包括的,位置一般都是用的迭代器。list反转和排序:lis.reverse(), lis.sort().
可以使用distance(a, b);计算a到b的距离。
迭代器和迭代器范围
iter.begin是指向第一个,iter.end是指向最后一个的下一个,实质end是不包括的最后一个的,所以()begin=end()的时候,容器是空的。iter+n只有vector和deque可以有这种操作。list不可以。迭代器是个指针,所以const_iterator是迭代器指向的值不会变。通常用常迭代器作为for循环的迭代,相当于for(int i=0;)中的i。
堆栈(容器适配器)
stack,LIFO形式。可以使用deque(默认) vector list做堆栈,stack<int, deque<int>> s。有empty() size() pop() top() push(item);push和pop都是始终指向栈顶。p.pop是删除数据,不返回删除的值,p.top()查看并返回查看的值。
队列(容器适配器)(堆栈和队列都没有迭代器,因为不能再中间进行操作数据,只能在两头进行,所以不能修改中间的数据)
queue, FIFO形式, queue不能用vector做队列(因为要求必须在两边进行操作,先进先出)。例如queue<int, list<int>> q; 或者指定为deque<int>(默认为deque)。操作,q.empty();q.size();q.front();查看队首,q.back();查看队尾,q.pop();q.push(item);push是在队尾操作,pop是在队首操作。
优先级队列(容器适配器)
priority_queue 不能使用list(因为要求能够对数据进行随机操作,而list不行),有最大最小优先级队列。priority_queue<int, deque<int>> pq;或者可以使用deque,vector(默认为), 不能使用list。pq.push(item),始终按循序排序(最大值(默认),或者最小值排序)。pq.size();获取大小数据个数。pq.top();查看,pq.pop();删除,因为队列始终指向队首,所以每次删除都是最大的(最小值)。pq.empty();判断是否为空。使用最小值优先级队列priority_queue<int ,deque<int>, greater<int>> pq;关键字greater<int>指示为最小值优先级队列。
顺序容器和容器适配器
vector list deque;stack queue priority_queue(优先级队列)
初始化问题使用默认的构造函数,带参数的构造函数
不同容器之间赋值,可以使用迭代器实现。
vector<int> ivec(ivec0); 使用同一种类型的顺序容器ivec0初始化ivec,这里ivec0必须是vector<int>类型。
但是list<string>slist(svec.begin(), svec.end());可以使用迭代器方式,对不同的容器赋值,例如svec是vector<string>类型。svec.begin()返回的是迭代器。svec.begin()+svec.size()/2 是指向svec向量对应的中间的数据元素。
list<string> slist(64);初始化了64个空字符串。或者list<string> slist(64, "hello");初始化为64个hello字符串。定义一个类Foo,然后使用vector<Foo> a(10);初始化10个Foo对象,
和vector<Foo> a;不一样,vector<Foo> a(10);调用默认的构造函数,所以如果Foo没有默认的构造函数,那就出错了。如果vector<Foo> a(10, 1);调用Foo的构造函数,对应的参数为1;后面的1为形参表。
另外还有array,表示固定大小的数组,不支持push_back insert,但是提供了其他的STL操作,begin(), end(),size()等。
valarray类模板,支持很多数值计算。可以实现数据直接数值操作。但是性能可能稍微损失。可以结合slice类,灵活使用里面的元素 slice(start, num, stride),例如varint[slice(0, 4, 3)] = 10; 表示赋值valarray变量varint的第0个开始,取4个,每次取的步长为3的对应的位置处的值为10.
initializer_list类,需要头文件initializer_list 头文件,一般用于将一系列数值传递给构造函数或者其他函数;例如 int sum(const std::initializer_list<int> &ril); 直接可以sum({2, 3, 4})。vector能够使用std::vector<int> vec = ({2, 3, 4})的原因,是vector构造函数使用initializer_list
作为构造函数参数。
顺序容器的操作(1)
vector list deque操作类似。容器定义的类型别名,vector<int>::size_type a1(常常替换for中的int a1); iterator const_iterator(const容器返回const迭代器) reverse_iterator(对应rbegin,rend) const_reverse_iterator difference_type value_type reference const_reference类型。
顺序容器的操作(2)
c.push_back(), c.push_front()(向量没有), c.insert(p, value),插在p所指元素的前面。 c.insert(p,num,value);在p的前面插入num个value。c.insert(p, beginp, endp);
顺序容器的操作(3)
关系运算,比较的容器必须具有相同的数据类型
顺序容易的操作(4)
size() 返回数据个数,max_size()返回最多保持多少数据,empty(), resize()等
顺序容器的操作(5)
访问元素(返回的是引用):c.back(),c.front(), c[n],c.at(n)后面两个只对vector和deque有效,对list没效果,并且使用at比用[]方式要好,特别是处理异常的时候。
而c.begin();返回的是迭代器,迭代器是指针。
vector<int>::reference a = ivec.front();
vector<int>::reference a = *ivec.begin();因为ivec.begin()返回的是迭代器,所以用指针。
顺序容易的操作(6)
删除元素 c.erase(p), c.erase(b, e), c.clear(),c.pop_back(),c.pop_front(). pop_front只适用于list和deque。
list<string>::iterator iter = find(b,e,"value"); e表示迭代器,删除中,不包括。s
顺序容器的操作(7)
赋值和交换c1=c2 c1.swap(c2)类型必须相同才能进行, c1.assign(b, t),类型兼容即可
vector容器的自增长
vector用数组做出来,但是有数组更多的优点。vecv.capacity()返回向量以供可以存放多少数据,但是这个容量是自增长的,每次 增加50%或者和使用的C++有关,不需要去控制,而vecv.size是数组里面存了多少数据。 reserve。
顺序容器的选用
vector deque(数组形式,所以插入删除操作会很慢,但是排序,查找很快) list是个链表,所以插入删除操作insert eraser很快,但是排序sort 查找binary_search很慢。 push_back对list很快。
构造string对象的方法
string s1(s2) s1(5, 'a') s1="str" s1(s.begin(), s.end())等等
修改string对象的方法
s.intert s.assign
string对象的比较
s.compare(s2)等等
map multimap也是容器,是红黑树结构
插入数据:map<int, string> a; a.insert(map<int, string>::value_type(1, "one")); a.insert(make_pair(-1, "two"));(最常用), a.insert(pair<int,string>(100, "one hundred")); a[1000]="one thousand";给map 插入数据赋值,通过键值对的方式;a.size();获取a有多少个键值对。所有容器都有迭代器都有对应的迭代器。可以通过迭代器获得对应的键值:map<int, string>::const_iterator i; i = a.begin(); i->first; i->second;获得对应的值,这里是int和string值;multimap 和map一样,只是需要放重复的数据的时候,得使用multimap,并且可以使用count(100)获取含有100的个数,另外第四种数组的形式,即a[1000]="one thounsand"不能在multimap中使用。
查找(因为map是红黑树结构,数据搜索查找会很快):map<int, string>::const_iterator i = a.find(100); if(i !=a.end()),{cout<<"no found the 100"}; 也可以直接映射,或者字典,或者关联数组。使用a[100]和使用a.find(100)一样,返回结果会是“one hundrred”。
删除:a.erase(100) if(a.rease(100)>0){cout<<"delete success"} a.erase(b, e); b和e分别是开始的迭代器和结束的迭代器。
set和multiset 是集和多集。也是一种容器,红黑树结构。set<int> a; 有操作insert, count find,erase。同样multiset也是可以允许重复,但是,set不允许重复。注意不能通过find进行修改,因为set和multiset每次插入数据,是自动进行了排序的。。
STL算法简介,100多种算法,函数对象,函数适配器,三个头文件#include <algorithm> <numeric> <functional>
函数对象简介
for_each(起始指针,结束指针,函数或者函数对象) ;例如for_each(ivec.begin(), ivec.end(), display()); greater<int>(), less<int>(),plus<int>() 等等,是一些预定义的函数对象。
将一个类作为函数对象,使用operator,函数对象的好处:一般比普通的函数要快,有自己的状态。
例如:以下是一个函数对象,调用的时候,可以直接PRINT();打印a的值,使用这个类,像使用函数一样
class PRINT{
public:
void operator()(int elem) const{ //调用的时候,自动调用里面的operator函数方法
cout<<elem<<'';
}
};
函数适配器 count_if(ivec.begin(). ivec.end(), bind2nd(greater<int>(), 4);其中bind2nd是预定义的函数适配器,表示大于4的数。
元素计算算法:
通用的count count_if 对所有的容器都可以用,速度相对慢些,例如count(ivec.begin(), ivec.end(), 4),计算这个ivec里面有几个4, count_if(ivec.begin(). ivec.end(), 函数或者函数对象),计算ivec中满足函数或者函数对象的数。关联容器的元素计算算法,例如set.count, map.count, multiset.count, multimap.count这个比通用的计算容器快些。
最小最大值算法:
min_element(b, e); min_element(b, e, op) 同理max。其中op为函数或者函数对象。
查找算法(1)
find find_if 查找效率比较慢,如果是已序空间的算法,一般用关联容器适配器自带的算法那。string类型不能使用find和find_if,查找的结果是一个迭代器。find(b, e, 4); 将找到的第一个4,返回对应的迭代器,指向相对应的值。
茶渣算法(2)
search_n(b, e, c, n)连续的大于n的数的起始位置。search_n(b, e, c, n,greaater<int>())连续c个大于n的数起始位置.
查找算法(3)
search() find_end(),是一对,search()重前面开始查找,find_end()从后面开始查找。search(l.begin(), l.end(), k.begin(),k.end());在l的开始到结束中,找k的开始到结束中的数据,例如从l(1 2 3 4 5 6)找k(4 5 6);返回在l中对应的起始位置。
查找算法(4)
find_first_of(b, e, sb, se); find_first_of(b, e, se, bp); 没有find_last_of(一般用逆向迭代器实现),在b,e之间找含有sb到se中的任意数,的起始位置,就可以了。所以存的是找到的在sb到se中第一个数据出现在b到e中的起始位置。
string查找函数和stl查找算法的比较,首先,这些方法都是string的成员函数,而stl中是对应的algorithm中的方法:string:find stl:find; string:rfind stl:find+逆向迭代器;string:find,stl:search;string:rfind, stl:find_end; string:find_first_of stl:find_first_of; string find_last_of,stl:find_first_of+逆向迭代器;逆向迭代器vector<int>::reverse_iter irve, irve.base();输出的是对应的正向的位置;string::npos,对应的string自定义的成员变量;
查找算法(5)
adjacent_find(b, e);查找连续两个相等的或者; adjacent_find(b, e, p);两个连续的复合谓词规则的两个;
查找算法要使用得当,例如,如果是已序区间,就用已序区间查找算法例如binary_search()等等。
查找算法(6)
已序区间查找,要求必须先排好序,这样查找就比较快,binary_search(b, e, v)二分法查找数字v,或者带有谓词p查找,binary_search(b, e, v, p); 区间查找:includes(b,e,sb,se)或者带有谓词includes(b,e,sb,se,p);,lower_bound(), upper_bound(), equal_range();
查找算法(7)(已序区间算法,都必须先排序)
lower_bound(b,e,v), upper_bound(b,e,v);lower找到值为v的第一个数的位置,这里返回的是index(从0开始),upper是找到值为v的最后一个位置,所以这两个算法主要用于查找到有序的数据中某个数据的位置后,方便插入运算。
equal_range()返回既包括lower_bound又包括upper_bound,返回的是一对迭代器,例如pair<list<int>::iterator, list<int::iterator>> range; range = equal_range(b, e, v);通过range.first()和range.second();
如果有关联式容器,那就不要用这些方法,因为关联式容器有对应的算法,性能更加;
for_each()算法:
for_each(b,e,p);可以遍历数据,和函数对象修改数据,以及使用for_each的返回值。如果p是函数对象,则返回值也是函数对象,例如 MeanValue mv = for_each(ivec.begin(),ivec.end(), MeanValue());
算法交换
swap_ranges(b,e,b2); 如果是全部数据交换,并且是相同数据类型交换,一把用容器的成员函数swap()
填充新值:(修改性算法)
fill(b,e,v),填充之后,原来的被替换掉了; fill_n(b,n,v) generate(b,e,p) generate_n(b,n,p)。v是值,p是函数对象。
替换算法:
replace(b,e,ov,nv); 把旧的值ov替换为新的值nv,replace_if(b,e,p,v) replace_copy(b1,e1,b2,ov,nv) replace_copy_if(b1,e1,b2,p,v)
删除算法(1):
remove(b,e,v) vemove_if(b,e,p) 这里是一种逻辑删除,不是真正的删除,这里返回的是删除的最后的一个元素的终点,因为,remove的删除操作仅仅是后面的复制到后面,来覆盖,没有移动的部分的值是不不变的。即list:1 2 3 4 5,remove(b,e,3)之后,变为1 2 4 5 4 5;
最后的两个一般使用容器的删除成员函数来删除,erase(end, list.end())才是真正的删除后面的4 5;
删除算法(2):
remove_copy() remove_copy_if(),把一个容器的数据复制到另外一个容器中,复制的过程中使用remove逻辑删除。
删除算法(3):
unique(b,e)或者 unique(b,e,p) unique_copy(b,e,newb) unique_copy(b,e,,newb,p);删除连续的重复的数字的算法或者删除连续的数字满足p条件的第二个数字的算法,注意没有unique_if和 unique_copy_if
逆转和旋转:
reverse() reverse_copy() rotate() rotate_copy()
算法排列组合:使用前,数据一般都需要先排序
next_permutation(b,e)下一个排列组合,如果返回值为true,表示排列组合还没有结束,还有排列组合,否则,就没有下一个排列组合了。 prev_permutation(b,e)与next
重排分区算法:
randon_shuffer(b,e)随机重排,就像洗扑克牌,重新打乱。 partition(b,e,p)把符合规则p的数据放在前面,其他的放在后面进行分区,并且分区以后,每个区域也是打乱重排了的,算法返回值为一个迭代器,即分区的分界的位置。stable_partition(b,e,p),稳定的分区,
即符合规则p的在前面,不符合规则的在后面,但是在每个区的相对顺序是不变的。
排序算法:
sort(b,e)默认从小到大排序 sort(b,e,p) stable_sort(b,e) stable_sort(b,e,p),注意:这里的排序算法不适合随机存取容器,因此不适合list容器,因为list容器不能随机存取。
局部排序:
partial_sort(b,se,e) b到e之间的数据排序,如果排序的序列中排到了se的位置,则后面的都不排序了,所以到底有多少个进行了排序不知道。partial_sort(b,se,e,p) partial_sort_copy(sb,se,db,de) partial_sort_copy(sb,se,db,de,p)
根据第n个元素进行排序:
nth_element(b,n,e) b到e之间排序,拍到第n个结束,所以知道有n个元素进行了排序。nth_element(b,n,e,p) 对比partial()算法。
堆排序算法:
make_heap(b,e)将向量变成堆,即二叉树规则进行排序。 push_heap(b,e)将一个数据加到堆里,这里前提是先将要push的数据push到原数据的对尾,然后进行二叉树排序形成新的堆。
pop_heap(b,e)将最大的,也就是树顶的元素放到了最后,其他的数据重新生成堆,进行了二叉树进行排序了。 sort_heap 对堆进行排序,形成普通的排序, 二叉树排序,