STL:standard template library 标准模板库,封装了很多实用的容器。
vector是一个容器。是个类。底层数据结构是数组。
vector:向量,变长数组,即“长度根据需要而自动改变的数组”。
使用前提:
#include
using namespace std;
1、vector定义
vector<typename> name;
以上是长度可以根据需要变化的一位数组,typename可以是任何基本类型,例如int,double,char,结构体。也可以是个容器。
vector<vector<int> > name;//注意> >之间要有空格,因为C++11前的编译器可能会把它视为移位
以上可视为一个两个维度都可变长的二维数组。
注意与下面这个区分:
vector<int> vi[100];
这种写法的一维长度固定为100,另一维是变长的。
2、vector容器内元素访问
(1)下标 vi[index] 0~vi.size()-1
(2)迭代器 类似指针
vector<typename>::iterator it;
it是一个vector::iterator型的变量。
*it是it指向的地址中的元素的值。
vi[i]等价于*(vi.begin()+i),只有在vector和string中才允许使用vi.begin()+i这种写法。
begin()函数取首元素地址,end()函数取微元素地址的下一个地址,它作为迭代器莫为标志,不存储任何元素。是左闭右开的思维。
遍历:
for(vector<int>::iterator it=vi.begin();it!=vi.end();it++)
{
printf("%d",*it);
}
``
```cpp
for(int i=0;i<vi.size();i++)
{
printf("%d",vi[i]);
}
3、vector常用函数
(1)vi.push_back() 在末尾插入一个元素 O(1)
(2)vi.pop_back() 删除尾元素 O(1)
(3)vi.size() 获得数组中元素的个数 O(1)
(4)vi.clear() 清空数组中所有的元素 O(n)
(5)vi.insert(vi.begin()+2,-1) 在vi[2]处插入 O(n)
(6)vi.erase(vi.begin()+3) 删除vi[3]处的元素 O(n)
vi.erase(vi.begin()+1,vi.begin()+4) 删除区间元素vi[1~3] 注意左闭右开 O(n)
其他用法 摘自 链接
3.at 得到编号位置的数据
4.begin 得到数组头的指针
5.end 得到数组的最后一个单元+1的指针
6.front 得到数组头的引用。注意,与vi.begin()是指针不同,vi.front()是元素的值。
7.back 得到数组的最后一个单元的引用
8.max_size 得到vector最大可以是多大
9.capacity 当前vector分配的大小
10.size 当前使用数据的大小
11.resize 改变当前使用数据的大小,如果它比当前使用的大,则填充默认值
12.reserve 改变当前vector所分配空间的大小
13.erase 删除指针指向的数据项
14.clear 清空当前的vector
15.rbegin 将vector反转后的开始指针返回(其实就是原来的end-1)
16.rend 将vector反转构的结束指针返回(其实就是原来的begin-1)
17.empty 判断vector是否为空
18.swap 与另一个vector交换数据
set是集合,元素自动递增排序,且自动去重。
set中的元素是唯一的,如果需要处理不唯一的情况,需要使用multiset,另外unordered_set以散列代替set内部的红黑树(一种自平衡二叉查找树),可以处理只去重但不排序的需求。
1、定义
#include
using namespace std;
set<typename> st;
set<int> a[100];
2、访问:只能用迭代器
set<typename>::iterator it;
for(it=st.begin();it!=st.end();it++)
{
printf("%d",*it);
}
3、常用函数
st.insert(100);//插入并自动递增排序和去重,时间复杂度o(logN).
set<int>::iterator it=st.find(2);//返回对应值为2的迭代器,o(logN)。
st.erase(st.find(2));//删除,o(1)
st.erase(100);//删除,o(logN)
st.erase(it,st.end());
printf("%d\n",st.size());//元素个数,o(1)
st.clear();//清空,o(n)
st.begin()+3是错误的,set不能这样写,只有vector和string可以。
#include
using namespace std;
string str;
string str="abcd";
for(int i=0;i<str.length();i++)
{
printf("%c",str[i]);
}
cin >> str;
cout << str;
printf("%s\n",str.c_str());
for(string::iterator it=str.begin();it!=str.end();it++)
{
printf("%c",*it);
}
printf("%c",*(str.begin()+3));
str3=str1+str2;//拼接赋值给str3
str1+=str2;//将str2直接拼接到str1上
//比较大小,比较规则是字典序
if(str1<str2)
if(str1!=str2)
if(str1>=str2)
printf("%d %d\n",str.length(),str.size());//size()与length()完全等同,遇到空字符不会被截断,可以返回字符串真实长度
str.insert(3,str2);//往str[3]处插入str2
str.insert(str.begin()+3,str2.begin(),str2.end());//[it2,it3)左闭右开
str.erase(str.begin()+4);
str.erase(str.begin()+2,str.end()-1);//左闭右开
str.erase(3,2);//删除从3号位开始的2个字符
str.clear();
cout << str.substr(0,5) << endl;//返回从pos号位开始、长度为length的子串
if(string::npos==-1) cout << "666";//string::npos是个常数等于-1或者4294967295,用以作为find函数失配时的返回值
//当str2是str的子串时,返回其在str中第一次出现的位置,如果不是则返回string::npos.
if(str.find(str2)!=string::npos) cout << str.find(str2);
//从str的pos位开始匹配str2,返回值同上,时间复杂度0(mn)
if(str.find(str2,7)!=string::npos) cout << str.find(str2,7);
str.replace(pos,len,str2);//把str从pos号位开始长度为len的子串替换为str2
str.replace(it1,it2,str2);//把str的迭代器[it1,it2)范围的子串替换为str2
注意区分:
size()、length()是c++中string的类的方法,只有string类的对象才可以用该方法,而字符串数组不可用,而strlen、strcpy等源于C语言的字符串处理函数库,需要include
size()与length()完全等同,遇到空字符不会被截断,可以返回字符串真实长度
strlen(),源于C语言,遇到空字符会截断,从而无法返回字符串真实长度
int main()
{
string name="babababa";
cout<<name.length()<<endl;
cout<<name.size()<<endl;
char mail[100]="haha";
cout<<strlen(mail)<<endl;
char *psd;
psd="lalala";
cout<<strlen(psd)<<endl;
return 0;
}
map译为映射,可以将任何基本类型映射到任何基本类型,包括STL容器。
映射前类型:键key;映射后类型:值value.
map底层是红黑树,会以键从小到大的顺序自动排序
map中的键(第一个参数)是唯一的;如果一个键需要对应多个值,就用multimap;r如果只映射而不按key排序,则用unordered_map,它以散列代替红黑树。
如果是int型映射到int型,就相当于是普通的int型数组。
如果是字符串映射到整形,必须使用string而不能用char数组。
#include
using namespace std;
map<typename1,typename2> mp;
map<string,int> mp;
map<set<int>,string> mp;
map<char,int> mp;
mp['c']=20;
for(map<char,int>::iterator it=mp.begin();it!=mp.end();it++)
{
printf("%c %d\n",it->first,it->second);
}
printf("%d\n",mp.size());
mp.clear();
队列,先进先出的容器,只能通过front()来访问队首元素,或通过back()来访问队尾元素。元素入队队尾,出队队首。
#include
using namespace std;
queue<typename> name;
queue<int> q;
for(int i=1;i<=5;i++)
{
q.push(i);
}
printf("%d %d\n",q.front());
for(int i=1;i<=3;i++)
{
q.pop();
}
if(q.empty()==true) printf("empty\n");
printf("%d\n",q.size());
使用front()和pop()函数前,必须用empty()判断队列是否为空。
还有两种队列:
双端队列:deque 首尾皆可插入和删除;
优先队列:priority_queue 使用堆实现的默认将当前队列最大元素置于队首的容器。
优先队列,底层是用堆实现的。
队首元素一定是当前队列中优先级最高的那一个。可以在任何时刻往优先队列里加入元素,但是堆会随时调整结构,使得每次的队首元素都是优先级最大的。
top()可获得队首元素,即堆顶元素;pop()令队首(堆顶)元素出队;
#include
using namespace std;
priority_queue<int> q;
q.push(3);
printf("%d\n",q.top());
q.pop();
if(q.empty()==true) printf("empty\n");
printf("%d\n",q.size());
如何定义优先队列内元素的优先级是运用好优先队列的关键。
(1)对于int,double,char等基本数据类型,优先队列对它们的优先级设置一般是数字大的优先级越高,因此队首元素就是优先队列内元素最大的那个;如果是char型,则是字典序最大的。
priority_queue<int> q;//与第二行的定义等价
priority_queue<int,vector<int>,less<int> > q;//数字大的优先级越大
priority_queue<int,vector<int>,greater<int> > q;//数字小的优先级越大
(2)结构体的优先级设置
按照水果的价格高的为优先级高,需要重载小于号“<”,注意重载大于号会编译错误。
struct fruit{
string name;
int price;
friend bool operator < (fruit f1, fruit f2){
return f1.price < f2.price;
}
};
如果要以价格低的水果为优先级高,那么只需要把return中的小于号改为大于号。
注意:此处对于小于号的重载和排序函数sort中的cmp函数有些相似,不过效果看上去似乎是“相反”的。在排序中,如果“return f1.price < f2.price”,那么价格是从高到低排序,但是在优先队列中却是把价格低的放在队首。原因在于,优先队列本身默认的规则就是优先级高的放在队首,因此把小于号重载为大于号的功能的时候只是把这个规则反向了一下。不妨直接记住。
也可以这样定义:
struct fruit{
string name;
int price;
}f1,f2.f3;
struct cmp{
bool operator()(fruit f1,fruit f2){
return f1.price > f2.price;
}
};
priority_queue<fruit, vector<fruit>, cmp> q;
如果结构体内的数据庞大,例如出现了字符串或数组,建议使用引用来提高效率,此时比较类的参数中需要加上“const”和“&”
friend bool operator < (const fruit &f1, const fruit &f2){
return f1.price < f2.price;
}
栈,后进先出。
#include
stack<typename> name;
stack<int> st;
for(int i=1;i<=5;i++)
{
st.push(i);
}
printf("%d\n",st.top());
st.pop();
if(st.empty()==true) printf("empty\n");
printf("%d\n",st.size());
如果用普通的函数来递归,一旦递归层数过深,则会导致程序运行崩溃。如果用栈来模拟递归算法的实现,则可以避免。
当想要将两个元素绑在一起作为一个合成元素、又不想因此定义结构体时,使用pair可以很方便地作为一个替代品。也就是说,pair实际上可以看成一个内部有两个元素的结构体,且这两个元素的类型是可以指定的。如下所示:
struct pair{
typename1 first;
typename2 second;
};
由于map的内部实现中涉及pair,因此添加map头文件时会自动添加utility头文件,所以可以偷懒直接添加map即可。
定义:
pair<typename1, typename2> name;
pair<string, int> p;
pair<string, int> p("haha",5);//定义并初始化
临时创建:
p=pair<string, int> ("haha", 5);
或
p=make_pair("haha",5);//自带的函数
访问:
pair<string, int> p;
p.first="haha";
p.second=5;
cout << p.first << p.second << endl;
比较操作数:
可以直接使用 ==、!=、<、>、<=、>=比较大小。
规则是先以first的大小作为标准,只有当first相等时才去判别second的大小。
if(p1<p3) printf("p1);
常见用途:
(1)用来代替二元结构体及其构造函数,可以节省编码时间。
(2)作为map的键值对来进行插入,如下:
map<string,int> mp;
mp.insert(make_pair("haha",5));
mp.insert(pair<string,int>("haha",10));
for(map<string,int>::iterator it = mp.begin();it!=mp.end();it++)
{
cout << it->first << " " << it->second << endl;
}