【c++】c++常用的STL,基于y总的语法基础课

#include <vector>

size()返回容器内元素的多少
empty()返回一个bool类型,表明vector是否为空。二者的时间复杂度都是O(1)。
所有的STL容器都支持这两个方法,含义也相同,之后我们就不再重复给出。

clear函数把vector清空除了队列queue、priority_queue、stack其他STL容器都有clear函数

迭代器

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

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

vector<int>::iterator it;

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

begin/end

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

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

下面两份代码都遍历了vector<int>a,并输出它的所有元素。

for (int i = 0; i < a.size(); i ++) cout << a[i] << endl;

//绝大部分都不会使用迭代器来访问vector
for (vector<int>::iterator it = a.begin(); it != a.end(); it ++) cout << *it << endl;

for (auto it = a.begin(); it != a.end(); it ++) cout << *it << endl;

for (int x : a) cout << x <<" ";

front/back 都是O(1)的时间复杂度

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

​ back函数返回vector的最后一个元素,等价于*==a.end() a[a.size() – 1]

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main(){
    vector<int> a({1,2,3});
    
    a.push_back(4);
    
    for(auto x : a) cout << x <<" ";
    cout<<endl;
    
    a.pop_back();
    for( auto x : a) cout << x << " ";
    cout<<endl;
    return 0;
}

//运行结果:
1 2 3 4 
1 2 3 

vector是基于倍增的思想

//比如说我这里定义了一个vector
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main(){
    vector<int> a({1,2,3});
    
    cout << a.capacity() << endl;
   
    cout << a.size() << endl; 
    
    return 0;
}

//运行结果
3
3

//我们发现vector的容器的容量和元素的个数大小一样
//那么,如果我插入一个元素这两个数字会发生变化吗?
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main(){
    vector<int> a({1,2,3});
    
    a.push_back(4);
    
    cout << a.capacity() << endl;
   
    cout << a.size() << endl; 
    
    return 0;
}

//运行结果
6
4

//我们就很容易发现vector的容器的大小变成了6,但此时元素的个数变成4
//容器的大小为什么会变成6呢?因为原来的容器大小是3,利用容器大小的倍增思想,每次发现插入的元素大于容器的大小,那么容器就会倍增2,然后把原来的元素复制到新扩大的vector容器里,来达到扩大容器的目的,这样就可以放入更多的元素;如此以往,如果再加的话会变成12……

我们如何分析vector的效率呢?(vector的原理)

我们发现我们新开的vector初始的容量大小就是元素的大小,但是随着插入元素的增多,容量会倍增,并且将原有的小的容器的元素复制到扩大的新的数组上来;复制元素效率不就下来了吗?

假如说我们插入了n个元素,那么vector会复制多少次呢?(复制一个元素算复制一次)

在插入的数量达到 n 2 \frac{n}{2} 2n,会复制 n 2 \frac{n}{2} 2n,那么在此之前就是 n 4 \frac{n}{4} 4n,(复制 n 4 \frac{n}{4} 4n次),然后一次类推vector复制了n( 1 2 \frac{1}{2} 21+ 1 4 \frac{1}{4} 41+ 1 8 \frac{1}{8} 81+……)

1 2 \frac{1}{2} 21+ 1 4 \frac{1}{4} 41+ 1 8 \frac{1}{8} 81+……<1(可以用数学证明,这里时间紧迫就不去证明了)

最后复制的次数会小于n

#include <queue>

主要包括循环队列queue和优先队列priority_queue一个堆,可以实时返回所有数的最大值两个容器

#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

int main(){
    queue<int> q;//队列
    priority_queue<int> a;//大根堆。每次弹出最大的值
    priority_queue<int, vector<int>, greater <int>> b;//小根堆,每次弹出最小的值
    
    //如果对结构体创建大根堆的话要重载小于号
    struct Rec{
      int a,b;
    //   //定义Rec结构体的大根堆要重载小于号
    //   bool operator < (const Rec& t) const{
    //       return a<t.a;
    //   }
        //定义Rec结构体的小根堆要重载大于号
         bool operator > (const Rec& t) const{
          return a>t.a;
      }
    };
    
    //priority_queue<Rec > d;//以Rec结构体为对象创建的d大根堆
    priority_queue<Rec ,vector<Rec>,greater<Rec>> d;//以Rec结构体为对象创建的d小根堆
    return 0;
}

普通队列用法:

#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

int main(){
    queue<int> q;//队列
    //只能在队头插入,在队尾弹出
    q.push(1);//在队头插入元素
    q.push(2);
    q.push(3);
    //插入的队列的顺序为(队尾)1、2、3(队头)
    q.pop();//弹出队尾元素
    cout<<q.front()<<endl;//返回队头元素
    cout << q.back() <<endl;//返回队尾元素
    return 0;
}

//运行结果
2
3

优先队列用法(以大根堆为例):

#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

int main(){    
    priority_queue<int > a;//大根堆
    
    a.push(1);
    a.push(3);
    a.push(2);
    
    cout<< a.top()<<endl;//取最大值
    a.pop();
    
    cout<< a.top()<<endl;
    return 0;
}

//运行结果
3
2

queue和priority_queue都不能使用clear()函数,那么清空队列?

q=queue<int>();//重新定义一个队列覆盖原有的队列达到清空的目的

#Include <stack>

先进后出的原则—这样的一个数据结构

#include <iostream>
#include <algorithm>
#include <stack>

using namespace std;

int main(){
    stack<int > stk;
    stk.push(1);//插入一个元素
    stk.push(2);
    stk.push(3);
    cout<< stk.top()<<endl;;//返回栈顶元素
    stk.pop();//弹出栈顶元素
    cout<< stk.top()<<endl;
    return 0;
}

//运行结果

3
2

#include <deque>

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

#include <iostream>
#include <algorithm>
#include <deque>

using namespace std;

int main(){
    deque<int> a;
    a.push_front(1);//在队头插入元素
    a.push_front(3);
    a.push_front(5);
    a.push_back(2);//在队尾插入元素
    a.push_back(4);
    a.push_back(6);
    //这个时候的deque存取的是:5 3 1 2 4 6
//    cout<< a.begin()<<"     "<<a.end()<<endl;//迭代器的begin()和end()
    
    cout<<a.front()<<"     "<<a.back()<<endl;//返回deque的队头和队尾元素
    for( int i=0;i<a.size();i++) cout<<a[i]<<" ";//可以使用[]访问deque的元素
    cout<<endl;
    
    a.pop_back();//弹出队尾元素
    a.pop_front();//弹出队头元素
    
    for( int i=0;i<a.size();i++) cout<<a[i]<<" ";
    cout<<endl;
    
    a.clear();//清空deque的元素
    cout<< a.size() <<endl;
    return 0;
}

//运行结果
5     6
5 3 1 2 4 6 
3 1 2 4 
0

#include <set>

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

红黑树不会QAQ,心态emo了

红黑树是一个有如下规则的二叉排序树

1.结点是红色或黑色。

2.根结点是黑色。

3.每个叶子结点都是黑色的空结点(NIL结点)。

4 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)

5.从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点。

具体红黑树的内容可以看漫画:什么是红黑树? - 掘金 (juejin.cn)

#include <iostream>
#include <algorithm>
#include <set>

using namespace std;

int main(){
    set<int> a;//元素不能重复
    multiset<int> b;//元素可以重复
    
    //也可以定义struct
    struct Rec{
      int x,y;
      //不过使用基于结构体的set集合的时候结构体内要重载小于号(因为set内部是有比较的)
      bool operator < (const Rec& t) const{
          return x<t.x;
      }
    };
    //重载<或者>等我们就可以定义一个Rec类型的set集合
    set<Rec> c;
    return 0;
}
//size()、empty()、clear()这三个函数的使用和效果和vector一样

迭代器:

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

设it是一个迭代器,例如set<int>::iterator it;

若把it++,则it会指向“下一个”元素。这里的“下一个”元素是指在元素从小到大排序的结果中,排在it下一名的元素。同理,若把it–,则it将会指向排在“上一个”的元素。(有序集合);时间复杂度是O(logn)

begin/end

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

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

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

#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
int main(){
    set<int> s;
    s.insert(1);//s.insert(x)把一个元素x插入到集合s中,时间复杂度为O(logn)。在set中,若元素已存在,则不会重复插入该元素,对集合的状态无影响。时间复杂度为O(logn)
    s.insert(2);
    s.insert(3);
    cout<< *s.find(1) <<endl;//s.find(x) 在集合s中查找等于x的元素,并返回指向该元素的迭代器。若不存在,则返回s.end()。时间复杂度为O(logn)。
    //因此我们可以使用if(s.find(x)==s.end())来判断是否能在set集合里找到x;时间复杂度为O(logn)
    
    if(s.find(4)==s.end())//判断x在a中的是否存在
    cout<<"4不存在集合s中"<< endl;
    
    cout<<*s.lower_bound(2)<<endl;//找到大于等于2的最小元素的迭代器;时间复杂度为O(logn)
    
    cout<<*s.upper_bound(2)<<endl;//找到大于2的最小的元素的迭代器;时间复杂度为O(logn)
    
    set<int>::iterator it=s.begin();
    
    s.erase(it);//删除迭代器it指向的元素;时间复杂度为O(logn)
    s.erase(2);//把所有值为2的元素;时间复杂度为O(k+logn)其中k为删除的元素的个数
    
    cout<<s.count(3)<<endl;//s.count(x) 返回集合s中等于x的元素个数,时间复杂度为 O(k +logn),其中k为元素x的个数。
    return 0;
}

//运行结果

1
4不存在集合s中
2
3
1

#include<unordered_set>

#include<iostream>
#include<algorithm>
#include<undered_set>
using namespace std;
int main(){
    //相对于set的有序集合,undered_set是一个无序集合
    undered_set<int> s;//底层实现是一个哈希表,不能存储重复元素
    //undered_set除了set中的lower_bound()和upper_bound()函数无法使用之外,其余函数都可以使用,时间复杂度都是O(1),但不支持二分
    undered_multiset<int> c;//底层实现是一个哈希表
    return 0;
}

#include <map>

map容器是一个键值对key-value的映射,其内部实现是一棵以key为关键码的红黑树。Map的key和value可以是任意类型,其中key必须定义小于号运算符。其实y总说还有一种mutimap(但是不经常用)这里就不说了

map是一个十分玄幻的容器----y总

#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
int main(){
    map<int,int> a;
    a[1]=3;
    a[520]=1314;
    cout<<a[520]<<endl;
    
    map<string,int> b;
    b["lml"]=38;
    cout<<b["lml"]<<endl;
    
    map<string,vector<int>> c;
    c["lml"]=vector<int>({1,2,3,4});
    c["wyq"]=vector<int>();
    
    cout<<c["lml"][1]<<endl;
    
    cout<<c["wyq"].size()<<endl;
    return 0;
}

//运行结果
1314
38
2
0

​ 声明

​ map<key_type, value_type> name;

​ 例如:

​ map<long, long, bool> vis;

​ map<string, int> hash;

​ map<pair<int, int>, vector> test;

size/empty/clear/begin/end均与set类似。

Insert/erase

​ 与set类似,但其参数均是pair<key_type, value_type>。插入的必须是一个二元组

#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
int main(){
    map<string,vector<int>> b;
    b.insert({"lml",{1,2,3,4}});
    cout<<b["lml"][1]<<endl;
    return 0;
}

//运行结果
2

h.find(x) 在变量名为h的map中查找key为x的二元组;返回的是一个迭代器

    cout<<(b.find("lml")==b.end())<<endl;

​ []操作符

​ h[key] 返回key映射的value的引用,时间复杂度为O(logn)。

[]操作符是map最吸引人的地方。我们可以很方便地通过h[key]来得到key对应的value,还可以对h[key]进行赋值操作,改变key对应的value。

#include <bitset>

#include<iostream>
#include<algorithm>
#include<bitset>
using namespace std;
int main(){
    bitset<1000> a,b;//定义长度为1000的01串
    a[0]=1;a[1]=1;
    cout<<a[0]<<" "<<a[1]<<" "<<a[2]<<endl;//没有设置为1的位置默认为0
    
    cout<<a.count()<<endl;//count()函数可以返回当前01串中1的个数
    a&=b;
    a|=b;//可以进行位运算
    
    b.set(3);//将下标为3的b的这一位上置1
    cout<<b[3]<<endl;
    b.reset(3);//将下标为3的b的这一位上置0
    cout<<b[3]<<endl;
    return 0;
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明璐花生牛奶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值