栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

浅谈STL容器

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

浅谈STL容器

STL容器 关于struct的更多
struct complex
{
    double real,image;
    void add(complex t)//
    {
        real=real+t.real;//real指当前节点的real( this->real ), t.real指参数complex t中的real
        image=image+t.image;
   }
    //一个简单的结构体成员函数
};

a.real=1,a.image=3;
b.real=3,b.image=4;
a.add(b);
cout<
    double real,image;
    complex() //默认构造函数
    {}
    complex(double a,double b) //用户自己定义的构造函数
    {real=a,image=b;}
    //另一种写法
    complex(double a,double b):real(a),image(b){}
    //推荐的写法——更快且能初始化常量
}a;


//使用构造函数赋值
complex a(1.1,2.0);
for(int i=0;i<100;i++) arr[i]=complex(i,i);

struct complex
{
   double real,image;
   friend bool operator==(complex a,complex b){
	    return a.real==b.real  &&  a.image==b.image;
    }//推荐的写法
    bool operator==(complex bb)
	{return (a == bb.a) && (b == bb.b);}
    //两种等号重载方法,其他也同理
    complex operator +(complex temp)
    {return complex(real+temp.real,img+temp.img);}
    //重载加号
}

//任何类型的数组都可以用sort进行进行排序,只要定义了这种类型的“<”运算。
//我们自定义的类型,如果定义了小于关系,则可以被sort当做基本数据类型来进行排序了
//简单的说,就是把cmp写到了结构体里面

在结构体外也能定义
bool operator==(complex a,complex b){return a.real==b.real  &&  a.image==b.image;}
迭代器
//让我们来复习指针先
int a[114514],b;
//指针指向变量在内存中的首地址
int *it=&b;//使用&取址符获取变量地址
*it=114514;相当于b=114514;
//因为it指向b,所以*it相当于b

it=a;//让it指向a的首地址(it=&a[0])
it++;//让it指向a[1]
it=a+3;//让it指向a[3]
//因为数组在内存中的地址是连续的
//所以it++能切换到a的下一个位置
//不要越界了!!!!!!

对于结构体,用->访问成员
struct node{int a,b};
node *it;
node b;
it=&b;
it->a=1;
it->b=2;


//迭代器同理
//可以看作是专用于STL的"高级特工指针"
//那么迭代器高级在何处
//例如,在STL中list的每一个元素存储在内存中的地址不一定是连续的
//这时用指针++访问下一个元素就会 :D
//但迭代器重载了++和--,使得++和--总是指向下一个或上一个位置


定义:

容器名<阿巴阿巴>::iterator 迭代器名;
vectora;//定义vector a
vector::iterator it;//定义用于vector的迭代器

it=a.begin();//让it指向a的首地址
it++;//it迭代到下一个位置
*(it+3)=114514//令it迭代到的第3个位置为114514
vectora;
it=a.begin()//让it指向a的首地址
(*it).a=114514  //访问元素
it->a=114514  //同上

//在STL中任意容器a.end()总是指向的是容器尾+1

//所以遍历时
for(auto i=a.begin();i!=a.end();i++)
{
	//阿巴阿巴阿巴阿巴阿巴阿巴
}
有的容器有反向迭代器
for(auto i=a.rbegin();i!=a.rend();i++)
{
    //阿巴阿巴阿巴阿巴阿巴
}

另外的补充(!必读!) 针对大多数容器
//什么是列表初始化(cpp11)
int a=1;//一般的初始方式
但是如果int a=一个大于int的数;
仍能运行,但是有编译错误
如果改成int a{一个大于int的数};
系统就会检查这里的类型是否合法

列表初始化还有一个用法,在系统已经确定此处的类型时,调用相应构造函数
stack>a;
如果不用列表初始化,就应a.push(make_pair(123,321));
但如果用就可以a.push({123,321});

//什么是auto
auto 让系统来判定此处的类型
auto x=123//此时系统会把此处的auto看作int
auto x='A'//此时系统会把此处的auto看作char
auto 的变量必须有初始值

//什么是引用
在交换两数时,我们会这样写
void swap(int &a,int &b){int t=a;a=b;b=t;}
swap(x,y);

这里的&,不是取址,表引用
在C中因为没有引用,会用指针代替,但指针不安全
所以c++中引入了引用这个概念,可以把它看作没有*的指针
int a=&b;a为b的引用
int *a=&b;指针a为b的地址

//使用指针达成同样的效果
void swap(int *a,int *b){int t=*a;*a=*b;*b=t;}
swap(&x,&y);


所有容器均能有构造函数:
    容器<阿巴阿巴>a(另一个相同类型的容器)
和其他的函数
    .clear()清空a
    .size()返回a的大小
    .max_size()返回a最大空间,array没有这个函数
    .empty()若a为空,返回1,否则返回0

迭代器使用

以vector为例
vectora;
vector::iterator it;//定义用于vector的迭代器
it=a.begin()令it等于a的首迭代器

//对于大多数容器,都有这几个返回迭代器的函数
a.begin()//开始
a.end()//结束
a.rbegin()//反转的开始
a.rend()//反转的结束

注:end()//指向的是末尾元素+1的迭代器

for(auto i=a.begin();i!=a.end();i++) cout<<*i< 
array(数组) 
#include
arraya;
//array必须的长度是constexpr的或准确的数字,

int a[114514];
//有区别吗[doge]

array可以用迭代器
arraya;
a.fill(123);让a内所有元素都为123
vector(动态数组 ,向量) 基本使用
#include
vectora;
vectora(2,3);//初始化为3个2
vectora(迭代器,迭代器)//定义,从别的容器初始化
有
        .push_back(x)//在尾部插入
        .pop_bakc()//删除末尾
        .front()//首的元素
        .back()//尾的元素
        .insert(迭代器,数)//插入
        .insert(迭代器,个数,数)//插入多个
        .insert(迭代器,另一个容器的迭代器,另一个容器的迭代器)
        //从别的容器插入
        .erase(迭代器)//删除
        .erase(迭代器,迭代器)//删除区间
        .at(x)//相当于a[x]
        .resize(数)//设置大小,多的地方用0填充
                        //   少的地方直接删除
重载了运算符
        []  用于像数组一样访问元素

a.swap(b);//交换
swap(a,b);//同上
内存管理与时间效率(选读)
.reserve(n)
使用reserve()函数提前设定容量大小,避免多次容量扩充操作导致效率低下。

当进行insert或push_back等增加元素的操作时,如果此时动态数组的内存不够用,
就要动态的重新分配当前大小的1.5~2倍的新内存区,再把原数组的内容复制过去。就会浪费时间

对于vector容器来说,如果有大量的数据需要进行push_back,
应当使用reserve()函数提前设定其容量大小,否则会出现许多次容量扩充操作,导致效率低下。

(1) size()告诉你容器中有多少元素。它没有告诉你容器为它容纳的元素分配了多少内存。
(2) capacity()告诉你容器在它已经分配的内存中可以容纳多少元素。
              那是容器在那块内存中总共可以容纳多少元素,而不是还可以容纳多少元素。
(3) resize(n)强制把容器改为容纳n个元素。调用resize之后,size将会返回n。
list(链表)
lista;
listlint1(a);//

        .push_back(x)//在尾部插入
        .push_front(x);//在头部插入
        .insert()//具有与vector相同的insert方法
        .erase()//具有与vector相同的erase方法
        .remove(x)//删除值为x的元素
        .remove_if(条件) 删除条件满足的元素,参数为自定义的函数
        .sort(); //专用的sort,默认升序
        .sort(cmp函数)//自定义条件排序
        .reverse()//反转list

//和sort的cmp用法一样
bool sortfunc(int a, int b) { return a > b ? 1 : 0; }
a.sort(sortfunc);

deque(双端队列) 双端队列,两端均可进队和出队
#include
!!!!!!!!!!!!!!!!!
deque没有reserve()
!!!!!!!!!!!!!!!!!
除此之外,deque拥有vector的所有构造和其他函数和重载运算符
dequea;
deque还有:
        .push_front(x)//在头部插入
        .pop_front//在头部删除

stack(栈)
dequea;
stack s(a);//可以用deque或stack初始化
有
    .top()//返回头的引用,可以看作返回一个值
    .pop()//出栈
    .push(x)//入
queue(队列)
dequea;
queue q1(a);//可以用deque或queue初始化
有
    .back()//返回尾的引用,可以看作返回尾的值
    .front()//返回头的
    q1.push(x)//入队
    q1.pop()//出队

补充:BFS (使用STL的queue)
while(!q1.empty())
{
    int h=q1.front()
    q1.pop()...
    for(...)
    {
        if(...)
        {
            q1.push(...)
            if(q1.back()....)
            {

            }
        }
    }
}

priority_queue(用优先队列实现的堆)
#include 
priority_queuea//大根堆
priority_queue,less > s1;//大根堆
priority_queue,greater > s;//小根堆
struct node {
    //对于自定义的结构,不要忘记重载运算符
    int x,y;
    //对于less,重载<,,堆顶为最大的
    friend bool operator<(node a, node b){return a.x < b.x;}
    //对于greater,重载>,,堆顶为最小的
    friend bool operator>(node a, node b){return a.x > b.x;}
};
对于priority_queue
        .top()返回堆顶
        .push(x)添加x到堆顶
        .pop()弹出堆顶
set&multiset

multi表能够重复元素

seta;//顺序遍历时是升序
set>a;//同上,但结构体必须用这种方法或下面的方法
set>aa;//顺序遍历时是降序

对于set有
    count(x)           返回某个值元素是否存在
    find(x)            返回迭代器,没有返回a.end
    erase(x)           删除
    erase(从迭代器,到迭代器)   删除
    insert(x)          在集合中插入元素
    lower_bound()   返回指向大于等于某值的第一个元素的迭代器
    upper_bound()   返回大于某个值元素的迭代器

set可以像数组一样遍历,顺序如上
for(auto i:a){}
for(auto i=a.begin();i!=a.end();i++){}

pair(双元组)
#include

pair p[1000];

p[0]={1,1};

p[0]=make_pair(1,1);

p[0].first=1;
p[0].second=1;

p[0]=pair(1,1);

pair相当于
struct pair{
    int first,second;
};

map&multimap

在set中,元素既是实值又是键值

但map中,键值和实值分开

注:键值用于索引的值,实值顾名思义

map相当于

bool operator<(paira,pairb){return a.first

set>a;

#include
mapa;//顺序遍历时是升序
map>b;//顺序遍历时是降序
map<键值类型,实值类型>a;
map内部用了pair
有
        .erase(x)删除键值为x的
        .erase(it)删除(迭代器)
        .count(x)找键值为x的,有返回1,否则0
插入元素
        a[1]=2//令键值为1的等于2
        .insert({1,2})//同上
        .insert(make_pair(1,2))//同上
map也可以向上面set一样遍历,但是按键值遍历
for(auto i=a.begin();i!=a.end();i++){}
for(auto i:a){}
//i的类型为pair
//.first是键值   ,  .second是实值
bitset(二进制的)

貌似用的不多

bitset不支持负数

bitset<1>可以当作bool来使用,所占空间更小

bool占一字节,bitset的每一位只占1bit,转int,string也更方便

bitset可用于表示大的二进制数(只要不转成int溢出就没事)

#include
bitset<3>a;定义3位的二进制数a
bitset<3>b(a);定义3位的二进制数b=a
bitset<3>b("110");定义b,使得b的值转化成二进制为110
bitset<3>c(114514);定义c,使得c的值转化成十进制为114514

bitset<3>a("100")
此时a[2]=1;
   a[1]=0;
   a[0]=0;

bitset的成员函数如下:
        .all()返回true如果所有位都为1
        .any()返回true如果有>=1个位为1
        .count()返回位为1的个数
        .none()返回的值与all()相反
        .flip()所有位取反
        .flip(x)第x位取反
        .set()全部变1
        .set(x)x位变1
        .reset()和.reset(x)与set同理,但是变0
        ._Find_first()返回从第0位找第一个1的位
        ._Find_next(x)同上,但是从x开始
        .to_string()返回一个bitset转string,
        .to_ullong()转unsigned long long
        .to_ulong()转unsigned int
    重载了
        ^,~,>>等位运算,但操作的的两个数必须都必须是位数相同的bitset

此外的此外[doge]

对于大多数stl容器,可以指定其内部实现方法(继承自上一个容器)

如stack,queue

我们可以把他们称之为适配器

比如stack内部默认用deque实现,但也可以指定它用vector来实现

stack>a;

但有的容器却不能这样,如queue不能构建于vector上,

因为vector不支持在最前面弹出元素

此外的此外的此外,

还是以stack为例

stack存在一个构造函数在默认deque的实现下

可以用deque来初始化stack

dequed;

stacka(d);

当然,改成用vector实现后也可以有

vectorv;

stack>a(v);

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/980132.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号