关于C++标准模板库(STL)的一些基本使用

vector

vector可以理解成变长数组,即长度根据需要而自动改变的数组

  • 头文件:#include <vector>
  • 定义:vector<typename>name;
  • vector内可以通过下标或者迭代器(iterator)访问(只有vector和string才允许使用v.begin()+3这种迭代器加整数的写法)
  • v.push_back(value) 时间复杂度:$O(1)$
  • v.pop_back() 时间复杂度:$O(1)$
  • v.size() 返回的是unsigned类型 时间复杂度:$O(1)$
  • v.clear() 时间复杂度:$O(N)$ $N$是vector中元素的个数
  • v.insert(it,value) 时间复杂度:$O(N)$
  • v.erase(it) 时间复杂度:$O(N)$
  • v.erase(first,last) 即删除[first,last)内元素 时间复杂度:$O(N)$

 

set

set可以理解成集合,一个内部自动有序且不含重复元素的容器

  • 头文件:#include <set>
  • 定义:set<typename>name;
  • set只能通过迭代器访问
  • s.insert(value) 时间复杂度:$O(logN)$ $N$为set内元素个数
  • s.find(value) 时间复杂度:$O(logN)$ $N$为set内元素个数
  • s.erase(it) 时间复杂度:$O(1)$
  • s.erase(value) 时间复杂度:$O(logN)$
  • s.erase(first,last) 时间复杂度:$O(last-first)$
  • s.size() 时间复杂度:$O(1)$
  • s.clear() 时间复杂度:$O(N)$
  • set中元素是唯一的,如果需要处理不唯一的情况,则需要使用multiset。另外,C++11标准中还增加了unordered_set,以散列代替set内部的红黑树(一种自平衡二叉查找树),使其可以用来处理只去重但不排序的需求,速度比set要快得多。

 

string

string是装载字符串的容器

  • 头文件:#include <string>
  • 定义:string str = "abcd";
  • string可以通过下标和迭代器进行访问
  • 可以使用c_str()将string类型转换为字符数组进行输出 printf("%s\n",str.c_str());
  • cin读入以遇到空格符便结束,getline(cin,str)以换行符为结束标志,因此可以读入空格
  • 使用'+' 可以拼接两个string。使用==,!=,<,<=,>,>=可以对两个string直接按照字典序进行比较
  • str.length() 或者 str.size() 可以返回string的长度 时间复杂度:$O(1)$
  • str.insert(pos,string) 时间复杂度:$O(N)$
  • str.insert(it,it2,it3) it为目的字符串的插入位置迭代器 it2和it3为待插字符串的首尾迭代器[it2,it3) 时间复杂度:$O(N)$
  • str.erase(it) 时间复杂度:$O(N)$
  • str.erase(first,last) 时间复杂度:$O(N)$
  • str.erase(pos,length) pos为开始删除的初始位置,length为删除字符的个数 时间复杂度:$O(N)$
  • str.clear() 时间复杂度:$O(1)$
  • str.substr(pos,len) 返回从pos开始,长度为len的子串 时间复杂度:$O(len)$
  • string::npos 这是一个常数,本身的值为-1,但由于是unsigned_int类型,因此也可以认为是unsigned_int类型最大值即4294967295。string::npos用作find()失配时的返回值。
  • str.find(str2) 返回str2在str中第一次出现的位置 或者 返回 string::npos 时间复杂度:$O(nm)$ 其中n和m分别为str和str2的长度
  • str.find(str2,pos) 从str的pos位开始匹配 同str.find(str2) 时间复杂度:$O(nm)$
  • str.replace(pos,len,str2) 把str从pos号位开始、长度为len的子串替换为str2 时间复杂度:$O(str.length())$
  • str.replace(it1,it2,str2) 把str[it1,it2)范围的子串替换为str2 时间复杂度:$O(str.length())$

 

map

map可以将任何基本数据类型(包括STL容器)映射到任何基本类型(包括STL容器),但若要表示字符串的话只能用string,而不能用char数组。

  • 头文件:#include <map>
  • 定义:map<typename1,typename2> m; typename1称为键 typename2称为值
  • map会按键的大小顺序自动排序,这是因为map内部是以红黑树实现的
  • m.find(key) 返回键为key的映射的迭代器,时间复杂度:$O(logN)$ N是map中映射的个数
  • m.erase(it) 时间复杂度:$O(1)$
  • m.erase(key) 时间复杂度:$O(logN)$ N是map中映射的个数
  • m.erase(first,last) 时间复杂度:$O(last-first)$
  • m.size() 时间复杂度:$O(1)$
  • m.clear() 时间复杂度:$O(N)$ N是map中元素的个数
  • mup的键和值是唯一的,如果一个键需要对应多个值,可以使用multimap。另外C++11标准中增加了unordered_map,以散列代替内部的红黑树实现,使其可以用来处理只映射而不按key排序的需求,速度比map要快得多

 

queue

queue实现了一个FIFO(先进先出)的容器

  • 头文件:#include <queue>
  • 定义:queue<typename> q;
  • queue中只能通过 q.front()来访问队首元素,q.back()来访问队尾元素 时间复杂度均为:$O(1)$
  • q.push(value) 时间复杂度:$O(1)$
  • q.pop() 时间复杂度:$O(1)$
  • q.empty() 时间复杂度:$O(1)$
  • q.size() 时间复杂度:$O(1)$
  • 使用q.front() 和 q.pop() 之前必须用q.empty()判断queue是否为空

 

priority_queue

priority_queue指优先队列,原理是用堆实现。在优先队列中,队首元素一定是优先级最高那个,然后这个优先级可以自己定义。

  • 头文件:#include <queue>
  • 定义:priority_queue<typename> pq;
  • priority_queue只能通过pq.top()来访问队首元素,之前要用pq.empty()进行判断 时间复杂度:$O(1)$
  • pq.push(value) 时间复杂度:$O(logN)$ N是当前队列里的元素个数
  • pq.pop() 记得之前要用pq.empty()进行判断 时间复杂度:$O(logN)$
  • pq.empty() 时间复杂度:$O(1)$
  • pq.size() 时间复杂度:$O(1)$
  • 当优先队列里面的元素为基本数据类型(int,double,char等等)时,默认是数字或字典序越大优先级越高所以越排在前面,而且以下两种定义是等价的:priority_queue<int> pq  以及 priority_queue<int,vector<int>,less<int> > pq,可以发现第二种定义方法多了两个参数,vector<int>是用来承载底层数据结构堆(heap)的容器,vector的数据类型与优先队列的保持一致,less<int>则是对第一个参数的比较类,less<int>表示数字越大优先级越大,相反,greater<int>表示数字越小优先级越大。
  • 然后讲如果数据类型是一个结构体的时候怎样设置优先级,当然当前类型即使是基本数据类型也可以使用这种方法,只不过第三个参数的写法不一样了。

 

struct fiuit
{
    string name;
    int price;
    friend bool operator < (fruit f1,fruit f2)
    {
        return f1.price < f2.price;
    }
};

 

 

 

现在希望按水果的价格高的为优先级高,那么就需要重载小于号"<",注意重载大于号会编译错误,因为从数学上来说只需要重载小于号,即$f1>f2$等价于判断$f2<f1$,而$f1==f2$等价于判断$!(f1<f2)\&\&!(f2<f1)$,函数内为return f1.price < f2.price ,因此重载后小于号还是小于号的作用。此时就可以直接定义fruit类型的优先队列:priority_queue<fruit> pq 其内部就是以价格高的水果为优先级高。相反,如果想以价格低的水果为优先级高,只要把return中的<改为>即可。没错,他们效果看上去与直觉相违背,不过语法上就是这样写的。我们可以理解成优先队列默认优先级高的在队首,那么假若我们把<改为>,则相当于把规则翻转了,那么原先大值优先也自然变成了小值优先。
还有一种方法是把重载的函数放在结构体外面:

 

struct cmp
{
    bool operator () (fruit f1,fuit f2)
    {
        return f1.price < f2.price;
    }
};

 

 

可以看到我们去掉了friend(友元),然后把<改为了一对小括号,记得要用struct把所有包起来。
还有在这个时候我们就不能写priority_queue<fruit> pq 来定义优先队列了,要改为priority_queue<fruit,vector<fruit>,cmp > pq 可以看到这跟上面基本元素的定义方法其实是可以差不多的。哪怕数据类型是其他STL容器也可以用这种重载符号的方法来使用priority_queue。
最后,如果结构体内的数据较大(使用了字符串或者很大的数组),我们可以使用“引用”来提高效率,即加上const和&,如下:

 

friend bool operator < (const fruit &f1,const fruit &f2)
{
    return f1.price < f2.price;
}

// 两种重载方法 

bool operator () (const fruit &f1,const fruit &f2)
{
    return f1.price < f2.price;
}

 

 

 

stack

stack是一个实现LIFO(后进先出)的容器。

  • 头文件:#include <stack>
  • 定义:stack<typename> s
  • stack中只能通过s.top()来访问栈顶元素 时间复杂度:$O(1)$ 注意先用s.empty()检测是否栈为空
  • s.push(value) 时间复杂度:$O(1)$
  • s.pop() 时间复杂度:$O(1)$
  • s.empty() 时间复杂度:$O(1)$
  • s.size() 时间复杂度:$O(1)$

 

pair

pair相当于一个内部有两个可以自定类型的元素的结构体(而不是真的需要用struct去实现,pair使这看起来更优美)。

  • 头文件:#include <utility> 值得注意的是map头文件包含utility头文件,记不住可以用map头文件代替,这也说明了map的内部实现用到了pair
  • 定义:pair<typename1,typename2> p  也可以同时进行初始化:pair<string,int> p("hello",5);
    如果想临时构建一个pair有两种方法:pair<string,int>("hello",5) 或者使用自带的make_pair("hello",5)
  • 关于pair中元素的访问跟结构体差不多,p.first 和 p.second
  • 两个pair类型数据可以直接用==、!=、<、<=、>、>=比较大小,规则是先比较first的大小,只有当first相等的时候才会去比较second的大小
  • pair常用来作为map的键值对数据

 

posted @ 2019-01-11 18:28  KachunYip  阅读(860)  评论(0编辑  收藏  举报