C++
C++三种容器:list、vector和deque的区别:https://blog.csdn.net/gogokongyin/article/details/51178378
一、容器
小常识
1、queue、stack不可遍历,list迭代器不可以使用"it=it+1",而可以使用"it++"
2、我们为什么这样写循环?
for(vector<int>::iterator iter = xx.begin(); iter != xx.end(); ++ iter)
{
...
}
如果是一个空的list,这样写会报错么?不会,因为空容器的begin与end的返回值是相同的,进不了循环。但是,需要注意的是,他们不是空指针,
std::vector::begin()
Returns an iterator to the first element of the container.
If the container is empty, the returned iterator will be equal to end().
std::vector::end()
Returns an iterator to the element following the last element of the container.
This element acts as a placeholder; attempting to access it results in undefined behavior.
3、erase()的写法
可以参考https://blog.csdn.net/sszgg2006/article/details/7453622
for(vector<int>::iterator it=vecInt.being(); it!=vecInt.end();){ //小括号里不需写 ++it
if(conditon){
it = vecInt.erase(it);
//以迭代器为参数,删除某元素及其迭代器并把数据删除后的下一个元素位置返回给迭代器。此时,不执行 //++it;
//这里it为实参,经转变为形参后,形参被删除,但是由于实参it是迭代器,是指针,所以实参it也成了野 //指针,所以需要重新赋值
}
else
{
++it;
}
}
此时,如果容器为空,erase()也没有没有问题。
1、vector
vector初始化
初始化方法
(1) vector
默认初始化,vector为空, size为0,表明容器中没有元素,而且 capacity 也返回 0,意味着还没有分配内存空间。这种初始化方式适用于元素个数未知,需要在程序中动态添加的情况。
(2): vector
vector
两种方式等价 ,ilist2 初始化为ilist 的拷贝,ilist必须与ilist2 类型相同,也就是同为int的vector类型,ilist2将具有和ilist相同的容量和元素
(3): vector
vector
ilist 初始化为列表中元素的拷贝,列表中元素必须与ilist的元素类型相容,本例中必须是与整数类型相容的类型,整形会直接拷贝,其他类型会进行类型转换。
(4): vector
ilist3初始化为两个迭代器指定范围中元素的拷贝,范围中的元素类型必须与ilist3 的元素类型相容,在本例中ilist3被初始化为{3,4,5,6}。注意:由于只要求范围中的元素类型与待初始化的容器的元素类型相容,因此迭代器来自不同的容器是可能的,例如,用一个double的list的范围来初始化ilist3是可行的。另外由于构造函数只是读取范围中的元素进行拷贝,因此使用普通迭代器还是const迭代器来指出范围并没有区别。这种初始化方法特别适合于获取一个序列的子序列。
(5): vector
默认值初始化,ilist4中将包含7个元素,每个元素进行缺省的值初始化,对于int,也就是被赋值为0,因此ilist4被初始化为包含7个0。当程序运行初期元素大致数量可预知,而元素的值需要动态获取的时候,可采用这种初始化方式。
(6):vector
指定值初始化,ilist5被初始化为包含7个值为3的int
vector复制vector
实现从vector1复制到vector2,实验证明,四种方式都实现了内存上的复制
(1) vector
(2) vector
(3) vector
(4) vector
vector5.resize(vector1.size());
copy(vector1.begin(),vector1.end(),vector5.begin());
方法四,使用std::copy(),前一定对新的vector进行resize(),否则异常
vector使用案例
vector<int>vector1 {1,2,3,4};
cout<<"元数据:"<<" ";
for_each(vector1.begin(),vector1.end(),[](int x){cout<<x<<" ";});
//构造函数(container)
vector<int>vector2(vector1);
reverse(vector2.begin(),vector2.end());
vector2[1]=111;
cout<<endl<<"构造函数(container): ";
for_each(vector2.begin(),vector2.end(),[](int x){cout<<x<<" ";});
cout<<"元数据:"<<" ";
for_each(vector1.begin(),vector1.end(),[](int x){cout<<x<<" ";});
//构造函数(container.begin(),container.end())
vector<int>vector3(vector1.begin(),vector1.end());
reverse(vector3.begin(),vector3.end());
vector3[1]=111;
cout<<endl<<"c.begin(),c.end(): ";
for_each(vector3.begin(),vector3.end(),[](int x){cout<<x<<" ";});
cout<<"元数据:"<<" ";
for_each(vector1.begin(),vector1.end(),[](int x){cout<<x<<" ";});
//赋值运算符重载=
vector<int>vector4=vector1;
reverse(vector4.begin(),vector4.end());
vector4[1]=111;
cout<<endl<<"赋值运算符重载= ";
for_each(vector4.begin(),vector4.end(),[](int x){cout<<x<<" ";});
cout<<"元数据:"<<" ";
for_each(vector1.begin(),vector1.end(),[](int x){cout<<x<<" ";});
//std::copy()
vector<int>vector5;
vector5.resize(vector1.size());
copy(vector1.begin(),vector1.end(),vector5.begin());
reverse(vector5.begin(),vector5.end());
vector5[1]=111;
cout<<endl<<"std::copy(): ";
for_each(vector5.begin(),vector5.end(),[](int x){cout<<x<<" ";});
cout<<"元数据:"<<" ";
for_each(vector1.begin(),vector1.end(),[](int x){cout<<x<<" ";});
/*
元数据: 1 2 3 4
构造函数(container): 4 111 2 1 元数据: 1 2 3 4
c.begin(),c.end(): 4 111 2 1 元数据: 1 2 3 4
赋值运算符重载= 4 111 2 1 元数据: 1 2 3 4
std::copy(): 4 111 2 1 元数据: 1 2 3 4
*/
vector的resize与reserve
reserve()函数为当前vector预留至少共容纳size个元素的空间.(译注:实际空间可能大于size)
resize() 函数( void resize( size_type size, TYPE val ) )改变当前vector的大小为size,且对新创建的元素赋值val
(翻译:
调整容器大小以包含count元素。
如果当前大小大于count,则容器将被缩减为其第一个count元素,就像重复调用pop_back()一样。
如果当前大小小于count,则附加元素并用值的副本初始化。)
resize和reserve函数本质都涉及了vector的内存存储空间,因为vector在内存中是连续存放的,所以当resize的空间大于现有的存储空间(capacity() 函数 返回当前vector在重新进行内存分配以前所能容纳的元素数量.)时,会重新选择更大的空间,并将所有元素复制过去。resize在初始化内存容量时有对值的初始化,所以此时push_back会产生size+1,内存容量不够,重新寻找更大的内存空间并复制所有元素,所以这个过程是很费事的。
插入测试
接下来探讨插入的效率的实例,分别尝试在插入大数据3.8GB和小数据380MB时,各种情况的实现。
(1)push_back直接插入
结论:费事,在插入的过程中,不断寻找“庇护所”,不断“迁移大本营”,舟车劳顿效率低下
void testPushBack_bigsize(){
vector<int> vector1;
clock_t start = clock();
for (int i = 0; i < 1000000000; ++i) {//3814MB
vector1.push_back(i);
}
cout <<"共耗时:"<< (clock() - start)/ CLOCKS_PER_SEC <<"s"<<endl;//共耗时:42s
cout <<"size:"<<vector1.size() << " capacity:" << vector1.capacity() << endl;//size:1000000000 capacity:1073741824
clock_t start2 = clock();
vector1.push_back(1);
cout <<"共耗时:"<< (clock() - start2)/ CLOCKS_PER_SEC <<"s"<<endl;//共耗时:0s
cout <<"size:"<<vector1.size() << " capacity:" << vector1.capacity() << endl;//size:1000000001 capacity:1073741824
}
(2)先reserve在push_back
结论:先分配空间再进行后续处理,能够有效的减少插入时间的损耗,耗时占原插入方式的1/3到1/2之间。
void testPushBack_byReserve_bigsize(){
vector<int> vector1;
vector1.reserve(1000000000);//3814MB
clock_t start = clock();
for (int i = 0; i < 1000000000; ++i) {
vector1.push_back(i);
}
cout <<"共耗时:"<< (clock() - start)/ CLOCKS_PER_SEC <<"s"<<endl;//共耗时:17s
cout <<"size:"<<vector1.size() << " capacity:" << vector1.capacity() << endl;//size:1000000000 capacity:1000000000
clock_t start2 = clock();
vector1.push_back(1);
cout <<"共耗时:"<< (clock() - start2)/ CLOCKS_PER_SEC <<"s"<<endl;//共耗时:76s
cout <<"size:"<<vector1.size() << " capacity:" << vector1.capacity() << endl;//size:1000000001 capacity:2000000000
}
void testPushBack_byReserve_smallsize(){
vector<int> vector1;
vector1.reserve(100000000);//381MB
clock_t start = clock();
for (int i = 0; i < 100000000; ++i) {
vector1.push_back(i);
}
cout <<"共耗时:"<< (clock() - start)/ CLOCKS_PER_SEC <<"s"<<endl;//共耗时:1s
cout <<"size:"<<vector1.size() << " capacity:" << vector1.capacity() << endl;//size:100000000 capacity:100000000
clock_t start2 = clock();
vector1.push_back(1);
cout <<"共耗时:"<< (clock() - start2)/ CLOCKS_PER_SEC <<"s"<<endl;//共耗时:2s
cout <<"size:"<<vector1.size() << " capacity:" << vector1.capacity() << endl;//size:100000001 capacity:200000000
}
(2)先resize在利用坐标进行赋值(相当于插入)
结论:在分配空间时直接对空间进行初始化,赋予初值,极大提升了存储的速率。但是在resize后进行push_back是不明智的选择。
void testinsert_byResize_bigsize(){
vector<int> vector1;
vector1.resize(1000000000);
clock_t start = clock();
for (int i = 0; i < 1000000000; ++i) {
vector1[i]=i;
}
cout <<"共耗时:"<< (clock() - start)/ CLOCKS_PER_SEC <<"s"<<endl;//共耗时:3s
cout <<"size:"<<vector1.size() << " capacity:" << vector1.capacity() << endl;//size:1000000000 capacity:1000000000
clock_t start2 = clock();
vector1.push_back(1);
cout <<"共耗时:"<< (clock() - start2)/ CLOCKS_PER_SEC <<"s"<<endl;//共耗时:66s
cout <<"size:"<<vector1.size() << " capacity:" << vector1.capacity() << endl;//size:1000000001 capacity:2000000000
}
void testinsert_byResize_smallsize(){
vector<int> vector1;
vector1.resize(100000000);
clock_t start = clock();
for (int i = 0; i < 100000000; ++i) {
vector1[i]=i;
}
cout <<"共耗时:"<< (clock() - start)/ CLOCKS_PER_SEC <<"s"<<endl;//共耗时:0s
cout <<"size:"<<vector1.size() << " capacity:" << vector1.capacity() << endl;//size:size:10000000 capacity:10000000
clock_t start2 = clock();
vector1.push_back(1);
cout <<"共耗时:"<< (clock() - start2)/ CLOCKS_PER_SEC <<"s"<<endl;//共耗时:2s
cout <<"size:"<<vector1.size() << " capacity:" << vector1.capacity() << endl;//size:10000001 capacity:20000000
}
vector优化问题
防止reallocate内存,而导致的数据拷贝产生的额外耗时
vector在push_back的时候,如果空间不足,会自动增补一些空间,如果没有预留的空间可用
就直接申请另一块可用的连续的空间,把数据拷贝过去,然后删除旧空间,使用新空间
结果造成效率低下 。
可以通过以下两种组合来防止reallocate.
-
vector::resize() 使用array index,效率最高,但是需要提前知道size大小
-
vector::reserve()使用 push_back(),效率一般,较原生有一定提升。
3、hash_map
hash函数->直接地址,比较函数->解决冲突
这两个参数刚好是我们在使用hash_map时需要指定的参数.
hash_map<int, string> mymap;
等同于:hash_map<int, string, hash
#include <hash_map>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
__gnu_cxx::hash_map<int,string> mymap;
mymap[1]="风琴杨";
mymap[2]="林青霞";
if (mymap.find(1)!=mymap.end())
cout<<mymap[1]<<endl;
cout<<mymap[10]<<" null"<<endl;
return 0;
}
//output
/*
风琴杨
null
*/
4、map(treemap)
int main() {
string A ="anagram";
string B ="nagaram";
map<char,int> mymap1;
map<char,int> mymap2;
for (int i = 0; i < A.size(); ++i) {
mymap1[A[i]]++;
mymap2[B[i]]++;
}
bool flag = true;
//遍历一
for (int j = 0; j <min(mymap1.size(),mymap2.size()) ; ++j) {
int a = mymap1[A[j]];
int b = mymap2[A[j]];
if (a!=b){
flag= false;
break;
}
}
//遍历二
map<char,int>::iterator it2 = map2.begin();
for (map<char,int>::iterator it = map1.begin();it!=map1.end(),it2!=map2.end(); ++it,++it2) {
if(it->second!=it2->second||it->first!=it2->first){
flag=0;
break;
}
}
cout<<flag<<endl;
return 0;
}
//两数之和
int main() {
int nums[]{2, 7, 11, 15};
int target = 9;
map<int,int> mymap;
for (int i = 0; i < sizeof(nums)/ sizeof(nums[0]); ++i) {
if(mymap.find(target-nums[i])!=mymap.end()){
cout<<(*mymap.find(target-nums[i])).second<<" "<<i;
break;
}
else
mymap[nums[i]]=i;
}
return 0;
}
增删改查、multimap是一个key对应多个值
class student{
public:
int age;
string name;
string bjclass;
student(string _name,int _age, string _bjclass){
name = _name;
age = _age;
bjclass = _bjclass;
}
student(){}
};
void multimap_test(){
cout<<"---------------multimap--------------"<<endl;
student s1,s2,s3,s4,s5;
s1.name="s1";
s1.bjclass="se1161";
s2.name="s2";
s2.bjclass="se1161";
s3.name="s3";
s3.bjclass="se1162";
s4.name="s4";
s4.bjclass="se1162";
s5.name="s5";
s5.bjclass="se1162";
multimap<string,student> mymap2;
// 软件1161
mymap2.insert(make_pair("软件1161",s1));
mymap2.insert(make_pair("软件1161",s2));
mymap2.insert(make_pair("软件1161",s3));
// 软件1161
mymap2.insert(make_pair("软件1162",s4));
mymap2.insert(make_pair("软件1162",s5));
for (multimap<string,student>::iterator it =mymap2.begin(); it!=mymap2.end() ; ++it) {
cout<<it->first<<" "<<it->second.name<<endl;
}
int se1161nums = mymap2.count("软件1161");
int se1162nums = mymap2.count("软件1162");
cout<<"软间1161人数:"<<se1161nums<<endl;
cout<<"软间1162人数:"<<se1162nums<<endl;
//遍历所有软件1161的学生
multimap<string,student>::iterator it2 =mymap2.find("软件1161");
int flag = 0;
while (flag<se1161nums){
cout<<it2->first<<" "<<it2->second.name<<endl;
it2++;
flag++;
}
cout<<"----------修改测试------------
";
//修改s3名称为ss3
for (multimap<string,student>::iterator it =mymap2.begin(); it!=mymap2.end() ; ++it) {
if(it->second.name=="s3")
it->second.name="ss3";
}
for (multimap<string,student>::iterator it =mymap2.begin(); it!=mymap2.end() ; ++it) {
cout<<it->first<<" "<<it->second.name<<" "<<it->second.bjclass<<endl;
}
}
int main() {
map<int,string> mymap1;
//插入方法1
pair<map<int,string>::iterator,bool> pair1 =mymap1.insert(pair<int,string>(1,"student1"));
if(pair1.second){
cout<<"key1插入成功
";
} else
cout<<"key1插入失败
";
mymap1.insert(pair<int,string>(2,"student2"));
//插入方法2
pair<map<int,string>::iterator,bool> pair2 = mymap1.insert(make_pair(1,"student3"));//若key已经存在则插入失败,返回pair的second为false
if(pair2.second){
cout<<"key2插入成功
";
} else
cout<<"key2插入失败
";
mymap1.insert(make_pair(4,"student4"));
//插入方法3
pair<map<int,string>::iterator,bool> pair3 = mymap1.insert(map<int,string>::value_type(5,"student5"));
if(pair3.second){
cout<<"key3插入成功
";
} else
cout<<"key3插入失败
";
mymap1.insert(map<int,string>::value_type(6,"student6"));
//插入方法4,若key已经存在会覆盖原值
mymap1[1]="student7";
mymap1[8]="student8";
for (map<int,string>::iterator it=mymap1.begin(); it !=mymap1.end() ; ++it) {
cout<<(*it).first<<":"<<(*it).second<<" ";
}
cout<<endl;
cout<<"------------------查找测试------------------
";
map<int,string>::iterator it = mymap1.find(4);
if (it!=mymap1.end())
cout<<"key:"<<it->first<<" value:"<<it->second<<endl;
else
cout<<"key4不存在!"<<endl;
pair<map<int,string>::iterator,map<int,string>::iterator> pair4 = mymap1.equal_range(4);
cout<<"第一个迭代器的位置(>=4):"<<(*pair4.first).first<<" "<<(*pair4.first).second
<<"
第二个迭代器的位置(>4):" <<(*pair4.second).first<<" "<<(*pair4.second).second<<endl;
cout<<"------------------删除测试------------------
";
while (!mymap1.empty()){
auto it = mymap1.begin();
cout<<(*it).first<<":"<<(*it).second<<" ";
mymap1.erase(it);//根据迭代器删除
}
multimap_test();
return 0;
}
5、set(treeset)
红黑二叉树变体
特点:集合 元素唯一 自动排序(默认从小到大) 不能按照[]方式插入元素
//两数之和
int main() {
int nums[]{2, 7, 11, 15};
int target = 9;
set<int> myset;
for (int i = 0; i < sizeof(nums)/ sizeof(nums[0]); ++i) {
if(myset.count(target-nums[i])==1){//判断是否存在target-nums[i]
cout<<i;
break;
}
else
myset.insert(nums[i]);
}
return 0;
}
包含仿函数、pair、查找等
void print(set<int> set1){
for (set<int>::iterator iter =set1.begin();iter!=set1.end();iter++) {
cout<<*iter<<" ";
}
}
void print3(set<int,greater<int>> set1){
for (set<int,greater<int>>::iterator iter =set1.begin();iter!=set1.end();iter++) {
cout<<*iter<<" ";
}
}
class student{
public:
int age;
string name;
student(string _name,int _age){
name = _name;
age = _age;
}
};
//仿函数
class Funstudent{
public:
bool operator()(const student &left,const student &rigth){
if(left.age<rigth.age)//按照年龄从小到达排序
return true;
else
return false;
}
};
void fanghanshu_pair(){
//仿函数
set<student,Funstudent> myset4;//针对class
student s1("s1",31);//插入s1成功
student s2("s2",14);//插入s2成功
student s3("s3",20);//插入s3成功
student s4("s1",56);//覆盖s1,插入成功
student s5("s5",56);//插入失败
//pair的使用
//pair<iterator,bool> insert( const TYPE& val );
pair<set<student>::iterator,bool> pair1 = myset4.insert(s1);
if(pair1.second)
cout<<"
插入s1成功"<<endl;
else
cout<<"插入s1失败"<<endl;
pair<set<student>::iterator,bool> pair2 = myset4.insert(s2);
if(pair2.second)
cout<<"插入s2成功"<<endl;
else
cout<<"插入s2失败"<<endl;
pair<set<student>::iterator,bool> pair3 = myset4.insert(s3);
if(pair3.second)
cout<<"插入s3成功"<<endl;
else
cout<<"插入s3失败"<<endl;
pair<set<student>::iterator,bool> pair4 = myset4.insert(s4);
if(pair4.second)
cout<<"插入s4成功"<<endl;
else
cout<<"插入s4失败"<<endl; myset4.insert(s4);
pair<set<student>::iterator,bool> pair5 = myset4.insert(s5);
if(pair5.second)
cout<<"插入s5成功"<<endl;
else
cout<<"插入s5失败"<<endl;
for (set<student,Funstudent>::iterator iter=myset4.begin(); iter !=myset4.end(); ++iter) {
cout<<iter->age<<" "<<iter->name<<endl;
}
}
void multiset_test(){
cout<<"---------------multiset--------------"<<endl;
multiset<int> set1;
set1.insert(100);
set1.insert(100);
set1.insert(50);
set1.insert(45);
set1.insert(10);
set1.insert(45);
set1.insert(200);
for (auto x:set1) {
cout<<x<<" ";
}
}
int main() {
//集合 元素唯一 自动排序(默认从小到大) 不能按照[]方式插入元素
set<int> myset1;//等价set<int,less<int>> myset2;从小到大排序
for (int i = 0; i < 5; ++i) {
int tmp = rand();
myset1.insert(tmp);
}
myset1.insert(100);
myset1.insert(100);//不可插入
myset1.insert(100);//不可插入
print(myset1);
cout<<endl;
cout<<"
---------------查找测试-------------------
";
set<int>::iterator it = myset1.find(100);
cout<<"*it:"<<*it<<endl;
myset1.count(100);
cout<<myset1.count(100)<<endl;//结果为1,查找值为100元素的位置
cout<<"lower_bound(100):"<<*myset1.lower_bound(100)<<endl;//结果为100,查找key大于等于100的迭代器
cout<<"upper_bound(100):"<<*myset1.upper_bound(100)<<endl;//结果为6334,查找key大于100的迭代器
//pair<iterator, iterator> equal_range( const key_type& key );
cout<<"equal_range(100):"<<*(myset1.equal_range(100).first)<<" "<<*(myset1.equal_range(100).second)<<endl;//结果为100 6334,等价于lower_bound+upper_bound
while (!myset1.empty()){
auto tmp = myset1.begin();
cout<<*tmp<<" ";
myset1.erase(myset1.begin());//删除首元素
}
cout<<"
从大到小排序的set:
";
set<int,greater<int>> myset3;//从大大小排序
for (int i = 0; i < 5; ++i) {
int tmp = rand();
myset3.insert(tmp);
}
myset3.insert(100);
myset3.insert(100);//不可插入
myset3.insert(100);//不可插入
print3(myset3);
cout<<endl;
while (!myset3.empty()){
auto tmp = myset3.begin();
cout<<*tmp<<" ";
myset3.erase(myset3.begin());//删除首元素
}
fanghanshu_pair();
multiset_test();
return 0;
}
6、deque
要点:双端数组
- std::distance()求坐标
- std::find()查找元素的迭代器,找不到返回 iter end()
void print(deque<int> dl) {
for (deque<int>::iterator iter = dl.begin(); iter != dl.end(); iter++) {
cout << *iter << " ";
}
cout << "
front:" << dl.front() << " end:" << dl.back() << endl;
}
int main() {
deque<int> dl;
dl.push_back(4);
dl.push_back(5);
dl.push_back(6);
dl.push_front(3);
dl.push_front(2);
dl.push_front(1);
print(dl);
dl.pop_back();
dl.pop_front();
print(dl);
auto x = find(dl.begin(),dl.end(),2);
if(x!=dl.end())
cout<<distance(dl.begin(),x)<<endl;//求坐标索引
else
cout<<"Not Found"<<endl;
return 0;
}
7、stack
class studnet{
public:
int age;
void printage(){
cout<<age<<" ";
}
};
int main() {
stack<int> st;
st.push(1);
st.push(2);
st.push(3);
st.push(4);
while (!st.empty()){
int temp = st.top();
cout<<temp<<" ";
st.pop();
}
cout<<endl;
stack<studnet> st2;
for (int i = 0; i < 5; ++i) {
studnet temp2;
temp2.age=i+20;
st2.push(temp2);
}
while (!st2.empty()){
studnet temp2 = st2.top();
temp2.printage();
st2.pop();
}
return 0;
}
/*输出
4 3 2 1
24 23 22 21 20
*/
8、queue
class studnet{
public:
int age;
void printage(){
cout<<age<<" ";
}
};
int main() {
queue<int> queue1;
queue1.push(1);
queue1.push(2);
queue1.push(3);
queue1.push(4);
while (!queue1.empty()){
int temp = queue1.front();
cout<<temp<<" ";
queue1.pop();
}
cout<<endl;
queue<studnet> queue2;
for (int i = 0; i < 5; ++i) {
studnet temp2;
temp2.age=i+20;
queue2.push(temp2);
}
while (!queue2.empty()){
studnet temp2 = queue2.front();//取队首
temp2.printage();
queue2.pop();
}
return 0;
}
/*
1 2 3 4
20 21 22 23 24
*/
priority_queue包含在同一个头文件 queue中,且方法与queue基本类似,不同之处在于没有了取队首、队尾元素的操作,只能取“优先级最高”的元素,方法蜜汁与stack相似~~,系统默认初始化为最大值队列
int main() {
priority_queue<int> pq1;//默认最大值优先队列
priority_queue<int,vector<int>,less<int>> pq2;//最大值优先队列,less、greater是预定义函数 可以看作是谓词
priority_queue<int,vector<int>,greater<int>> pq3;//最小值优先队列
pq1.push(1);
pq1.push(4);
pq1.push(2);
pq1.push(3);
pq1.push(6);
cout<<"最大值优先队列 ->队首元素:"<<pq1.top();//队首元素是优先级最高的元素,方法与栈类似top
cout<<" 队列长度:"<<pq1.size()<<endl;
while (pq1.size()>0){
cout<<pq1.top()<<" ";
pq1.pop();//删除队首元素
}
pq3.push(1);
pq3.push(4);
pq3.push(2);
pq3.push(3);
pq3.push(6);
cout<<endl<<"最小值优先队列->队首元素:"<<pq3.top();
cout<<" 队列长度:"<<pq3.size()<<endl;
while (pq3.size()>0){
cout<<pq3.top()<<" ";
pq3.pop();//删除队首元素
}
return 0;
}
/*
最大值优先队列 ->队首元素:6 队列长度:5
6 4 3 2 1
最小值优先队列->队首元素:1 队列长度:5
1 2 3 4 6
*/
9、list(双向链表)
void print(list<int> li) {
for (list<int>::iterator iter = li.begin(); iter != li.end(); iter++) {
cout << *iter << " ";
}
cout << "
front:" << li.front() << " end:" << li.back() << endl;
}
class studnet{
public:
int age;
void printage(){
cout<<age<<" ";
}
};
int main() {
list<int> li;
for (int i = 5; i < 10; ++i) {
li.push_back(i);//尾插法
}
print(li);
for (int i = 4; i >= 1; --i) {
li.push_front(i);//头插法
}
print(li);
auto x = find(li.begin(),li.end(),5);
if (x!=li.end())
cout<<"5对应的坐标:"<<distance(li.begin(),x)<<endl;//list的结点index序号从0位置开始
//x=x+1;//出错,list迭代器不支持随机访问,因为内部实现是双向链表
x++;//不报错
x++;//不报错
x++;//不报错
li.insert(x,88);//x=7,则插入后原来7号变8号
print(li);
li.erase(li.begin(),++li.begin());//[,),不删除结尾元素
print(li);
li.push_back(88);
print(li);
li.remove(88);//删除所有为88的结点
print(li);
return 0;
}
/*
5 6 7 8 9
front:5 end:9
1 2 3 4 5 6 7 8 9
front:1 end:9
5对应的坐标:4
1 2 3 4 5 6 7 88 8 9
front:1 end:9
2 3 4 5 6 7 88 8 9
front:2 end:9
2 3 4 5 6 7 88 8 9 88
front:2 end:88
2 3 4 5 6 7 8 9
front:2 end:9
*/
约瑟夫环
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <list>
using namespace std;
int main() {
list<int> persons;
int personnum, number;
scanf("%d%d", &personnum, &number);
for (int i = 0; i < personnum; ++i)
persons.push_back(i + 1);
int target = 0;
for (list<int>::iterator it = persons.begin(); it != persons.end();) {
target++;
if (target == number) {
cout << *it << " ";
it = persons.erase(it);//这步易错
target = 0;
} else it++;
if (it == persons.end())
it = persons.begin();
cout << (persons.begin() == persons.end());
//3 6 9 2 7 1 8 5 10 4
}
return 0;
}
10、string
字符串的输入
(1)用的 string 的 getline
getline(cin,str)函数是处理string类的函数。第二个参数为string类型的变量。读入时第二个参数为string类型,而不是char*,要注意区别
正确写法:
string str;
while (true) {
getline(cin, str);
if (str == "")
break;
/**下面这种方式也可以
if(str.lenght()==0)
break;
*/
cout << str << endl;
}
注意,下面这种格式的写法,只按回车并不会结束输入,首先getline从标准输入设备上读入字符,然后返回给输入流cin,while判断语句的真实判断对象是cin,也就是判断当前是否存在有效的输入流,这种情况下,不管你怎么输入都跳不出循环,因为你的输入流有效,跳不出循环
错误写法:
while(getline(cin,str))
{
cout<<str<<endl;
}
cin输入后缓冲区的处理
在cin和get的输入中,结束符都没有被写入目标中,但是他们存在差异:
cin与getline()的区别:
getline()中的结束符,结束后,结束符不放入缓存区;
cin的结束符,结束后,结束符还在缓存区;
在这中情况下,下面实例测试输入1个整数50和一个字符串“test”,字符串str实质接收的是换行符,str被赋值为“”字符串。
错误写法:
string str;
int num;
cin >> num;
getline(cin, str);
cout << str << endl;
所以在使用 cin 后若要使用 getline() 必须要把前面cin遗留的结束符处理掉,解决方法为:在使用getline()之前,加入一行getline()来处理cin留下的结束符;代码如下:
正确写法:
string str;
int num;
cin >> num;
getline(cin, str);
getline(cin, str);
cout << str << endl;
字符串中的查找与替换
string.substr(pos,length)从pos截取length个字符,返回字符串
string.replace(pos,length,source),用source替换从pos开始的length的字符
string findReplaceString(string S, vector<int>& indexes, vector<string>& sources, vector<string>& targets) {
if(indexes.empty())
return S;
for (int i = 0;i<indexes.size();i++){
// if(S[indexes[i]]==sources[i][0]){
if(S.substr(indexes[i],sources[i].size())==sources[i]){
S.replace(indexes[i],sources[i].size(),targets[i]);
for(int &x :indexes){
if(x>indexes[i])
x +=(targets[i].size()-sources[i].size());
}
}else
continue;
}
return S;
}
二、Algorithm
1、函数对象(谓词)
入门程序
template <class T>
class FuncShowElem{
public:
int sum = 0;
void operator()(T &t){
cout<<t<<" ";
sum++;
}
void prinfsum(){
cout<<"
"<<this->sum;
}
};
template <class T>
void FuncShowElem2(T &t){
cout<<t<<" ";
}
int main() {
FuncShowElem<int> funcShowElem;//函数对象
vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);
vector1.push_back(4);
cout<<"
遍历1:";
for_each(vector1.begin(),vector1.end(),FuncShowElem<int>());//匿名函数对象
cout<<"
遍历2:";
funcShowElem = for_each(vector1.begin(),vector1.end(),funcShowElem);//函数对象,返回函数对象,注意传入的是形参,再返回形参
funcShowElem.prinfsum();
cout<<"
遍历3:";
for_each(vector1.begin(),vector1.end(),FuncShowElem2<int>);//通过回调函数,谁使用回调函数,谁写回调函数的入口地址
return 0;
}
/*输入
遍历1:1 2 3 4
遍历2:1 2 3 4
4
遍历3:1 2 3 4
*/
一元谓词示例
class isDiv{
public:
int div;
bool operator()(T &t) {
return (t%div==0);
}
isDiv(int _div){
this->div = _div;
}
};
int main() {
vector<int> vector1;
for (int i = 1; i < 10; ++i) {
vector1.push_back(i);
}
isDiv<int> isDiv1(4);
vector<int>::iterator iter ;
iter = find_if(vector1.begin(),vector1.end(),isDiv1);
if (iter!=vector1.end()){
cout<<"vector中第一个被4整除的数"<<*iter<<endl;
} else
cout<<"vector中不存在被四整出的元素"<<endl;
return 0;
}
//输出
//vector中第一个被4整除的数4
//实现字符串转大写
string str1("helloworld");
transform(str1.begin(),str1.end(),str1.begin(),::toupper);
cout<<str1<<endl;
//HELLOWORLD
二元谓词
求和,transform
template <class T>
class SumAdd{
public:
T operator()(T t1,T t2) {
return t1+t2;
}
};
int main() {
vector<int> vector1;
vector<int> vector2;
vector<int> vector3;
for (int i = 1; i < 10; ++i) {
vector1.push_back(i);
}
for (int i = 10; i < 20; ++i) {
vector2.push_back(i);
}
//resize() 函数改变当前vector的大小为size,且对新创建的元素赋值val
vector3.resize(9);
vector<int>::iterator iter;
transform(vector1.begin(),vector1.end(),vector2.begin(),vector3.begin(),SumAdd<int>());
for (iter= vector3.begin(); iter!=vector3.end(); ++iter) {
cout<<*iter<<" ";
}
return 0;
}
/*
11 13 15 17 19 21 23 25 27
*/
排序
bool MyComp(int &a,int &b){
return a>b;
}
void show(int a){
cout<<a<<" ";
}
int main() {
vector<int> vector1;
for (int i = 0; i < 20; ++i) {
vector1.push_back(rand()%100);
}
for (auto x :vector1) {
cout<<x<<" ";
}
cout<<endl;
sort(vector1.begin(),vector1.end(),MyComp);//二元谓词,定义比较元素的函数
for_each(vector1.begin(),vector1.end(),show);//回调函数作谓词,输出vector
return 0;
}
/*输出
41 67 34 0 69 24 78 58 62 64 5 45 81 27 61 91 95 42 27 36
95 91 81 78 69 67 64 62 61 58 45 42 41 36 34 27 27 24 5 0
*/
class compareFun {
public:
bool operator()(const string& string1, const string& string2) {
string temp1;
string temp2;
temp1.resize(string1.size());
temp2.resize(string2.size());
transform(string1.begin(),string1.end(),temp1.begin(),::tolower);
transform(string2.begin(),string2.end(),temp2.begin(),::tolower);
return temp1<temp2;
}
};
int main() {
set<string> set1;
set1.insert("aaaa");
set1.insert("bbbb");
set1.insert("cccc");
set1.insert("dddd");
set1.insert("eeee");
set<string>::iterator iter = set1.find("aAaa");
if (iter != set1.end())
cout << "查找到" << endl;
else
cout << "没有查找到" << endl;
set<string, compareFun> set2;
set2.insert("aaaa");
set2.insert("bbbb");
set2.insert("cccc");
set2.insert("dddd");
set2.insert("eeee");
set<string>::iterator iter2 = set2.find("aAaa");
if (iter2 != set2.end())
cout << "查找到" << endl;
else
cout << "没有查找到" << endl;
}
/*
没有查找到
查找到
*/
预定义函数对象
#include <iostream>
#include <algorithm>
#include <set>
#include <functional>
using namespace std;
int main() {
plus<int> intAdd;
int x = 10;
int y = 20;
int z = intAdd(x,y);
cout<<z;
plus<string> stringAdd;
string s1 = "aaa";
string s2 = "bbb";
string s3;
s3 = stringAdd(s1,s2);
cout<<"
"<<s3;
cout<<endl;
vector<string> vector1;
vector1.push_back("bbb");
vector1.push_back("aaa");
vector1.push_back("ddd");
vector1.push_back("ccc");
//从大到小排序
sort(vector1.begin(),vector1.end(),greater<string>());
for_each(vector1.begin(),vector1.end(),[](string x){cout<<x<<" ";});//匿名函数
//计数
vector1.push_back("ccc");
vector1.push_back("ccc");
//函数适配器,equal_to有两个参数,bind2nd将预定义函数和参数绑定
int result = count_if(vector1.begin(),vector1.end(),bind2nd(equal_to<string>(),"ccc"));
cout<<"
"<<result;
return 0;
}
/*
30
aaabbb
ddd ccc bbb aaa
3
*/
函数适配器
bool greater2(const int &a){
return a>3;
}
class mygreater{
public:
int target = 3;
bool operator()(const int& a){
return a>target;
}
};
/*
* template<typename _Operation, typename _Tp>
inline binder2nd<_Operation>
bind2nd(const _Operation& __fn, const _Tp& __x)
{
typedef typename _Operation::second_argument_type _Arg2_type;
return binder2nd<_Operation>(__fn, _Arg2_type(__x));
}
*/
int main() {
vector<int> vector1;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
for_each(vector1.begin(),vector1.end(),[](int x){cout<<x<<" ";});//匿名函数
int num = count(vector1.begin(),vector1.end(),3);
cout<<endl<<num<<endl;
//通过谓词求大于3 的个数
int result1 = count_if(vector1.begin(),vector1.end(),greater2);//回调函数
int result2 = count_if(vector1.begin(),vector1.end(),mygreater());//自定义函数对象
//通过函数适配器bind2nd指定预定义函数对象的参数,将二元谓词转变为一元谓词
int result3 = count_if(vector1.begin(),vector1.end(),bind2nd(greater<int>(),3));//预定义函数对象
int result4 = count_if(vector1.begin(),vector1.end(),not1(bind2nd(greater<int>(),3)));//预定义函数对象
cout<<"回调函数:"<<result1<<" 自定义函数对象:"<<result2<<" 预定义函数对象:"<<result3<<endl;
cout<<"谓词求小于2 的个数"<<result4<<endl;
return 0;
}
/*
0 1 2 3 4 5 6 7 8 9
1
回调函数:6 自定义函数对象:6 预定义函数对象:6
4
*/
常用算法
for_each()
注意,for_each()是修改性算法,即可以对元素进行修改,返回值是函数对象
/*
* template<typename _InputIterator, typename _Function>
_Function
for_each(_InputIterator __first, _InputIterator __last, _Function __f)
{
// concept requirements
__glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
__glibcxx_requires_valid_range(__first, __last);
for (; __first != __last; ++__first)
__f(*__first);
return _GLIBCXX_MOVE(__f);
}
*/
class showElem{
public:
int num = 0;
void operator()(const int& a){
cout<<a<<" ";
num++;
}
void printNums(){
cout<<num<<endl;
}
};
int main() {
vector<int> vector1;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
for_each(vector1.begin(),vector1.end(),[](int x){cout<<x<<" ";});//匿名函数
cout<<"
";
//函数对象做参数、函数对象做返回值
showElem showElem1 = for_each(vector1.begin(),vector1.end(),showElem());//函数对象,只有foreach返回函数对象
cout<<"
";
showElem1.printNums();
for_each(vector1.begin(),vector1.end(),[](int &x){x++;});//匿名函数,修改元素值
for_each(vector1.begin(),vector1.end(),[](int &x){x++;});//匿名函数,修改元素值
for_each(vector1.begin(),vector1.end(),[](int x){cout<<x<<" ";});//匿名函数
return 0;
}
/*
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
10
2 3 4 5 6 7 8 9 10 11
*/
transform()
与for_each()不同的是,transform的函数对象(回调函数)必须有返回值
int main() {
vector<int> vector1;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
for_each(vector1.begin(),vector1.end(),[](int x){cout<<x<<" ";});//匿名函数
cout<<"
";
list<int> mylist;
mylist.resize(vector1.size());
transform(vector1.begin(),vector1.end(),mylist.begin(),bind2nd(multiplies<int>(),10));
//函数对象做参数、函数对象做返回值
for_each(mylist.begin(),mylist.end(),[](int x){cout<<x<<" ";});//匿名函数
return 0;
}
/*
0 1 2 3 4 5 6 7 8 9
0 10 20 30 40 50 60 70 80 90
*/
查找算法
adjacent_find
int main(){
vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);
vector1.push_back(3);
vector1.push_back(4);
vector1.push_back(5);
vector1.push_back(2);
vector<int>::iterator iter = adjacent_find(vector1.begin(),vector1.end());
if (iter!=vector1.begin()){
cout<<"值为:"<<*iter<<" "<<"下标位置:"<<distance(vector1.begin(),iter);
} else
cout<<"没有找到相似元素!";
cout<<endl;
auto i2 = std::adjacent_find(vector1.begin(), vector1.end(), std::greater<int>());
if (i2 == vector1.end()) {
std::cout << "The entire vector is sorted in ascending order
";
} else {
std::cout << "The last element in the non-decreasing subsequence is at: "
<< std::distance(vector1.begin(), i2) << '
';
}
return 0;
}
/*
值为:3 下标位置:2
The last element in the non-decreasing subsequence is at: 5
*/
find
int main(){
vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);
vector1.push_back(4);
vector1.push_back(5);
vector<int>::iterator iterator1= find(vector1.begin(),vector1.end(),3);
if (iterator1!=vector1.end()){
cout<<"找到了,"<<"下标位置:"<<distance(vector1.begin(),iterator1);
} else
cout<<"没有找到";
return 0;
}
//找到了,下标位置:2
find_if
int main(){
vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);
vector1.push_back(4);
vector1.push_back(5);
vector<int>::iterator iterator1= find_if(vector1.begin(),vector1.end(),[](int x){ return x>3;});
if (iterator1!=vector1.end()){
cout<<"找到了,"<<*iterator1<<" 下标位置:"<<distance(vector1.begin(),iterator1);
} else
cout<<"没有找到";
return 0;
}
//找到了,4下标位置:3
binary_search
int main(){
vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);
vector1.push_back(3);
vector1.push_back(4);
vector1.push_back(5);
vector1.push_back(2);
bool flag = binary_search(vector1.begin(),vector1.end(),4);
if (flag){
cout<<"找到了";
} else
cout<<"没有找到!";
return 0;
}
count
int main(){
vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);
vector1.push_back(3);
vector1.push_back(4);
vector1.push_back(5);
vector1.push_back(2);
int nums= count(vector1.begin(),vector1.end(),3);
if (nums==0){
cout<<"找到了";
} else
cout<<"有找到"<<nums<<"个3";
return 0;
}
/*
有找到2个3
*/
count_if
int main(){
vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);
vector1.push_back(4);
vector1.push_back(5);
int nums= count_if(vector1.begin(),vector1.end(),[](int x){ return x>3;});
if (nums==0){
cout<<"找到了";
} else
cout<<"大于3有"<<nums<<"个";
return 0;
}
//大于3有2个
max_element
max_element() 和 min_element(),这个的话还是蛮好用的,比自己一个循环写下来要快的多了,取容器中的最大最小值
int num[] = { 2, 3, 1, 6, 4, 5 };
cout << "最小值是 " << *min_element(num, num + 6) << endl;
cout << "最大值是 " << *max_element(num, num + 6) << endl;
cout << "最小值是 " << *min_element(num, num + 6, cmp) << endl;
cout << "最大值是 " << *max_element(num, num + 6, cmp) << endl;
/*
最小值是 1
最大值是 6
最小值是 1
最大值是 6
*/
排序算法
merge
int main(){
vector<int> vector1;
vector<int> vector2;
vector<int> vector3;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
for (int i = 1; i < 10; i+=2) {
vector2.push_back(i);
}
vector3.resize(vector1.size()+vector2.size());
merge(vector1.begin(),vector1.end(),vector2.begin(),vector2.end(),vector3.begin());
for_each(vector3.begin(),vector3.end(),[](int x){cout<<x<<" ";});
return 0;
}
//0 1 1 2 3 3 4 5 5 6 7 7 8 9 9
sort
class student{
public:
string name;
int age;
student(){}
student(string _name,int _age){
age=_age;
name = _name;
}
};
int main(){
vector<student> vector1;
student student1("xiaosun",22);
student student2("laoli",21);
student student3("gaozong",23);
vector1.push_back(student1);
vector1.push_back(student2);
vector1.push_back(student3);
for_each(vector1.begin(),vector1.end(),[](student s){cout<<s.name<<" "<<s.age<<endl;});
sort(vector1.begin(),vector1.end(),[](student a,student b){ return a.age>b.age;});
cout<<"-----------
";
for_each(vector1.begin(),vector1.end(),[](student s){cout<<s.name<<" "<<s.age<<endl;});
return 0;
}
/*
xiaosun 22
laoli 21
gaozong 23
-----------
gaozong 23
xiaosun 22
laoli 21
*/
rand_shuffle
int main(){
vector<int> vector1;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
for_each(vector1.begin(),vector1.end(),[](int s){cout<<s<<" ";});
random_shuffle(vector1.begin(),vector1.end());
cout<<"
-----------
";
for_each(vector1.begin(),vector1.end(),[](int s){cout<<s<<" ";});
return 0;
}
/*
0 1 2 3 4 5 6 7 8 9
-----------
8 1 9 2 0 5 7 3 4 6*/
reverse
int main(){
vector<int> vector1;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
for_each(vector1.begin(),vector1.end(),[](int s){cout<<s<<" ";});
reverse(vector1.begin(),vector1.end());
cout<<"
-----------
";
for_each(vector1.begin(),vector1.end(),[](int s){cout<<s<<" ";});
return 0;
}
/*
0 1 2 3 4 5 6 7 8 9
-----------
9 8 7 6 5 4 3 2 1 0
*/
拷贝替换算法
copy
int main(){
vector<int> vector1;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
vector<int> vector2;
vector2.resize(vector1.size());
copy(vector1.begin(),vector1.end(),vector2.begin());
for_each(vector2.begin(),vector2.end(),[](int s){cout<<s<<" ";});
return 0;
}
//0 1 2 3 4 5 6 7 8 9
replace
int main(){
vector<int> vector1;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
replace(vector1.begin(),vector1.end(),1,11);
for_each(vector1.begin(),vector1.end(),[](int s){cout<<s<<" ";});
return 0;
}
replace_if
int main(){
vector<int> vector1;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
replace_if(vector1.begin(),vector1.end(),[](int x){ return x>5;},11);
for_each(vector1.begin(),vector1.end(),[](int s){cout<<s<<" ";});
return 0;
}
//0 1 2 3 4 5 11 11 11 11
swap
int main(){
vector<int> vector1;
vector<int> vector2;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
for (int j = 10; j < 20; ++j) {
vector2.push_back(j);
}
for_each(vector1.begin(),vector1.end(),[](int s){cout<<s<<" ";});
for_each(vector2.begin(),vector2.end(),[](int s){cout<<s<<" ";});
swap(vector1,vector2);
cout<<"
-----------------
";
for_each(vector1.begin(),vector1.end(),[](int s){cout<<s<<" ";});
for_each(vector2.begin(),vector2.end(),[](int s){cout<<s<<" ";});
return 0;
}
/*
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
-----------------
10 11 12 13 14 15 16 17 18 19 0 1 2 3 4 5 6 7 8 9*/
accumulate
int main(){
vector<int> vector1;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
for_each(vector1.begin(),vector1.end(),[](int s){cout<<s<<" ";});
int result = accumulate(vector1.begin(),vector1.end(),0);
cout<<endl<<result;
return 0;
}
/*
0 1 2 3 4 5 6 7 8 9
45*/
fill
int main(){
vector<int> vector1;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
for_each(vector1.begin(),vector1.end(),[](int s){cout<<s<<" ";});
fill(vector1.begin(),vector1.end(),1);
cout<<endl;
for_each(vector1.begin(),vector1.end(),[](int s){cout<<s<<" ";});
return 0;
}
/*
0 1 2 3 4 5 6 7 8 9
1 1 1 1 1 1 1 1 1 1*/
集合运算
set_union
int main(){
vector<int> vector1;
for (int i = 0; i < 10; ++i) {
vector1.push_back(i);
}
vector<int> vector2;
for (int i = 3; i < 15; ++i) {
vector1.push_back(i);
}
vector<int> vector3;
vector3.resize(vector1.size()+vector2.size());
set_union(vector1.begin(),vector1.end(),vector2.begin(),vector2.end(),vector3.begin());
for_each(vector3.begin(),vector3.end(),[](int s){cout<<s<<" ";});
return 0;
}
//0 1 2 3 4 5 6 7 8 9 3 4 5 6 7 8 9 10 11 12 13 14