C++ STL常见容器(vector list deque 顺序容器篇)

c++中有两种类型的容器:顺序容器和关联容器

顺序容器主要有:vector、list、deque等。其中vector表示一段连续的内存地址,基于数组的实现,list表示非连续的内存,基于链表实现。deque与vector十分相似,采用dynamic array来管理元素,提供随机访问,但是deque的dynamic array头尾两端都开放,可以在头尾两端快速安插和删除。

关联容器主要有map和set等。map是key-value形式的,set是单值,只保存key,且map和set都会根据关键字自动排序,因为底层实现都是红黑树(RB-tree),红黑树是平衡二叉搜索树,两者皆是根据key来排序,自然也就不允许有重复的key(multimap multiset允许可重复key),也不允许修改key值。

除此之外,实际上还有三个容器适配器:stack,queue和priority_queue。stack和queue基于deque实现,priority_queue基于vector实现。适配器会对容器的接口进行重新封装,这样做一方面可以通过改装实现一些特定属性,例如stack的先进后出,就是将deque两端开放封装成一端开放,即隐藏deque的部分接口(push_front,pop_front等),另一方面其实正如适配器的本身的意义一样,提高容器的适用度,容器适配器stack或者queue都是常用的数据结构,具备基本的pop push等功能。

容器类自动申请和释放内存,我们无需new和delete操作

vector

记得我第一次见到这个容器时,我们管它叫做“向量”,也确实这个容器就像是向量一样可以存储多种数据类型的数据,且如同向量一样可以增长,实际上它的底层实现是数组,之所以可以做到增大,是因为在新增数据的时候,若数组满了,就要分配一块更大的内存,将原来的数据复制过来,释放之前的内存,在插入新增的元素。

下面写了几个,用法比较简单,但是如果想了解更多可以查看它的源码,里面很多地方的实现非常简洁巧妙。

#include <iostream>
#include <vector>
using namespace std;
int main() {

    vector<int> vec;
    //vector():创建一个空vector

    vector<int> vec2(10);
    //vector(int nSize):创建一个vector,元素个数为nSize

    vector<int> vec3(10,1);
    //vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t

    vector<int> vec4(vec3);
    //vector(const vector&):复制构造函数

    int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    vector<int> vec5(a,a+10);
    //vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中

    //1.push_back 在数组的最后添加一个数据

    //2.pop_back 去掉数组的最后一个数据

    vec5.at(5);
    //3.at 得到编号位置的数据

    //for(vector<int>::iterator iter = vec5.begin(); iter != vec5.end();iter++)
    for(auto iter = vec5.begin(); iter != vec5.end();iter++)
        cout<<*iter<<" ";
    cout<<endl;
    //4.begin 得到数组头的指针
    //5.end 得到数组的最后一个单元+1的指针

    cout<<vec5.front()<<endl;
    //6.front 得到数组头的引用

    cout<<vec5.back()<<endl;
    //7.back 得到数组的最后一个单元的引用

    //8.max_size 得到vector最大可以是多大

    cout<<vec5.capacity()<<endl;
    //9.capacity 当前vector分配的大小

    //10.size 当前使用数据的大小

    //11.resize 改变当前使用数据的大小,如果它比当前使用的大,者填充默认值

    //12.reserve 改变当前vecotr所分配空间的大小

	auto ite = vec5.begin();//ite指向begin
    advance(ite, 3);//ite向右移动3位,指向begin+3,即3
    ite = vec5.erase(ite);//begin+3被删除后,其后的数组向前平移一位,ite依然指向begin+3,但此时是数字4
    //13.erase 删除指针指向的数据项

    //14.clear 清空当前的vector

    //15.rbegin 将vector反转后的开始指针返回(其实就是原来的end-1)

    //16.rend 将vector反转构的结束指针返回(其实就是原来的begin-1)

    cout<<vec5.empty()<<endl;
    //17.empty 判断vector是否为空

    vec2.swap(vec5);
    //18.swap 与另一个vector交换数据
	
	//19.insert 插入数据,向__position前插入__x,具体细节就不展开了,看参数命名大概能想到
    /**
     *
    insert(const_iterator __position, const value_type& __x)
    insert(const_iterator __position, value_type&& __x)
    insert(const_iterator __position, initializer_list<value_type> __l)
    insert(const_iterator __position, size_type __n, const value_type& __x)
    insert(const_iterator __position, _InputIterator __first, _InputIterator __last)
     */

    return 0;
}

list

list 容器是由双向链表实现的。所以不能实现快速随机访问,具体表现为不能通过at()和[ ]访问。
记一下list几个特殊操作。

函数名描述
push_front将元素插入开头
push_back将元素插入结尾
pop_front删除开头元素
pop_front删除结尾元素
splice合并两个链表
merge合并两个排序链表,若两个链表有序合并后仍有序
unique删除链表内重复的元素
remove删除链表内指定的元素
remove_if删除满足条件的元素
sort将链表排序
reverse将链表反序
#include <iostream>
#include <list>
using namespace std;
// a predicate implemented as a function:
bool single_digit (const int& value) { return (value<10); }
int main() {

    list<int> lis(10,13);
    int arr[5] = {9, 18, 5, 3, 11};
    list<int> lis2(arr, arr+5);
    lis.sort();
    lis2.sort();
    lis.merge(lis2);
//  在__position之前插入另一个链表
//    lis.splice(lis.begin(), lis2);
//    lis.unique();
//  lis.sort();
    lis.remove(9);//去掉9
    lis.remove_if(single_digit);//去掉小于10的数字
//    简单的说就是将链表内的元素依次经过_Predicate检查,若返回真则去掉该元素
//    当然,实际上这个函数可以类似于python中map函数那样使用,对链表内的元素依次加工,但并不推荐这样做

    /**
    * lis.remove_if(_Predicate);
    *
    *  @brief  Remove all elements satisfying a predicate.
    *  @tparam  _Predicate  Unary predicate function or object.
    *
    *  Removes every element in the list for which the predicate
    *  returns true.  Remaining elements stay in list order.  Note
    *  that this function only erases the elements, and that if the
    *  elements themselves are pointers, the pointed-to memory is
    *  not touched in any way.  Managing the pointer is the user's
    *  responsibility.
    */
    for(auto iter = lis.begin(); iter != lis.end();iter++)
        cout<<*iter<<" ";
    return 0;
}

deque

deque最大的特点就是两端开放,这一点和list相似,但是内部的实现是截然不同的,deque底层数据结构是分段数组,具体为:

deque由一些独立的区块组成,第一区块朝某方向扩展,最后一个区块朝另一方向扩展。它允许较为快速地随机访问但它不像vector一样把所有对象保存在一个连续的内存块,而是多个连续的内存块,并且维护护一个存放这些数组首地址的索引数组,如下图所示:
在这里插入图片描述
由图可知,实际上deque的随机访问效率会低于vector,因为vector是连续的,而deque是分段的。deque不像vector那样当内存不足时需要数据转移,deque会申请新的分段存储新的数据,并将新的分段首地址放入索引数组内。

deque的各项操作只有一下两点和vector不同:

deque不提供容量操作:capacity()和reverse()。

deque直接提供函数完成首尾元素的插入和删除。

其他均与vector相同。

如果想了解更全面的C++STL方面的知识可以点击链接。cplusplus参考手册

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值