C++学习笔记-STL标准模板库(二)STL容器类

【1】 序列式容器

vector与deque的迭代器支持算术运算,list的迭代器只能进行++/–操作,不支持普通的算术运算。

1.vector

引用头文件<vector>用于存储连续的元素,数组,定义一维数组,如下,

vector<int> pos(32, -1);//定义32个字符
vector<char> a(32);
	a.pop_back();
	a.push_back(0);

首先这是动态的,非常灵活。储存的时候是连续的线性空间,支持高效的随机访问和在尾端插入/删除操作,但其他位置的插入/删除操作效率低下。

class Vector
{
	public:
		iterator start;
		iterator begin(){return start};
        //目前使用了的空间头
        
		iterator finish;
		iterator end(){return finish};		
        //目前使用了的空间的尾
        
        size_type size(){ return size_type(end-start);}
        
        reference front() {return *begin();}
        reference back(){return *(end()-1);}
		iterator end_of_storage;	//目前可用空间的尾
		
    	bool empty(){return begin()==end();}
}

当我们用push_back插入的时候,会检查 还有没有备用的空间 ,如果没有的话
要经历allocator先按规则分配空间 然后把之前的拷贝过去, 再插入,最后释放原来的空间 这是一个庞大的工程
分配空间的规则
{

如果原本大小为0

​ 分配空间为1;

else

​ 分配空间为=原来的*2;

}

2.deque

引用头文件<deque>,双端队列
我们知道连续内存的容器不能随意扩充,因为这样容易扩充别人那去。deque却可以,它创造了内存连续的假象。其实deque由一段一段构成 ,他是分段连续,而不是内存连续 当走向段的尾端时候自动跳到下一段 所以支持迭代器++ 操作,自动跳到下一段的方法由operator++实现。deque每次扩充 则申请一个段,双端队列底层是一段假象的连续空间,实际是分段连续的,“整体连续”的假象。可以头插和尾插,也可以弹出队首和队尾。

	b.push_front(0);
	b.push_front(0);
	b.pop_back();
	b.pop_front();

如果只是简单的存储元素使用vector就可以了,如果对元素任意位置进行插入或者删除操作比较多,则使用list即可,所以一般很少使用deque;deque的最大应用,就是用其作为标准库中stack和queue的底层结构。

3.list

引用头文件<list>,list底层是一个带头节点的双向循环链表,任意位置插入和删除时间复杂度0(1)。

【2】适配器容器

stack

引用<stack>,栈,先进后出,stack(栈)是一种后进先出的数据结构 ,最后加入栈的元素将最先被取出来,在栈的同一端进行数据的插入与取出,这一段叫做“栈顶”。由于栈只是进一步封装别的数据结构,并提供自己的接口,所以代码非常简洁,如果不指定容器,默认是用deque来作为其底层数据结构,stack(栈)的常用操作:top(),push(elem),pop(),empty(),size();

queue

引用<queue>,队列,先进先出,queue(队列)也是一种逻辑数据结构,其具有先进先出的特性,只能在队的前端进行删除, 在队的后端进行插入。queue也可以看成是容器的容器,内部是使用其它容器来存放具体数据,然后加限制,使得我们的数据操作只能是在头或尾。从尾部添加数据,从头部取数据,从而实现FIFO的特性。同stack一样,内部默认数据存放容器为deque,若要用非默认容器初始化,必须要在模板中指定容器类型。

priority_queue

引用<ueue>,具有优先级的队列,在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。底层数据结构通常采用堆数据结构来实现。

模版原型:priority_queue<T,Sequence,Compare>
priority_queue<int, vector<int>, greater<int>> c;
实现优先级队列的底层容器,默认是vector;
第三个参数是c++比较函数。默认情况下是less;

【3】关联式容器

1.set,unordered_set,multiset

集合(set)是一种包含已排序对象的关联容器,不允许有重复元素。无序集合(unordered_set)则元素不重复且无序。set(<set>)和unordered_set(<unordered_set>)底层分别是用红黑树和哈希表实现的。multiset(<set>)它可以保存重复的元素,这些数据有序。这意味我们总可以插入元素,当然必须是可接受的元素类型。默认用 less 来比较元素,但也可以指定不同的比较函数。

红黑树:
优点:很多操作在lg(n)的时间复杂度下就可以实现,因此效率非常的高
缺点: 空间占用率高,因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间

红黑树,Red-Black Tree 「RBT」是一个自平衡(不是绝对的平衡)的二叉查找树(BST),树上的每个节点都遵循下面的规则:
性质1:每个节点要么是黑色,要么是红色。
性质2:根节点是黑色。
性质3:每个叶子节点(NIL)是黑色。
性质4:每个红色结点的两个子结点一定都是黑色,没有两个相邻的红色节点(并没有说不能出现连续的黑色节点)
性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

平衡二叉树
平衡二叉树,又称AVL树,指的是左子树上的所有节点的值都比根节点的值小,而右子树上的所有节点的值都比根节点的值大,且左子树与右子树的高度差最大为1。因此,平衡二叉树满足所有二叉排序(搜索)树的性质。插入和删除节点需要重新平衡,主要有四种调整方式:LL(左旋)、RR(右旋)、LR(先左旋再右旋)、RL(先右旋再左旋)。

哈希表:
优点:查找速度非常的快
缺点: 哈希表的建立比较耗费时间

哈希表
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
不管hash函数设计的如何巧妙,总会有特殊的key导致hash冲突,特别是对动态查找表来说。
hash函数解决冲突的方法有以下几个常用的方法
1.开放定制法
2.链地址法
3.公共溢出区法
建立一个特殊存储空间,专门存放冲突的数据。此种方法适用于数据和冲突较少的情况。
4.再散列法
准备若干个hash函数,如果使用第一个hash函数发生了冲突,就使用第二个hash函数…

typedef struct  {
	char* Key;
	char* Value;
	hash_node* next;
}hash_node;//节点

typedef struct {
	int size;
	int count;
	hash_node* node;
}hash_table;//表

2. map,unordered_map,multimap

map(<map>)是STL的一个关联容器,一对多映射,基于关键字快速查找,不允许重复值,对象是模板类,需要关键字和存储对象两个模板参数,其中第一个参数称为关键字,每个关键字只能在map中出现一次;第二个参数称为该关键字的值,可理解为“{键,值}对” 。map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的。
unordered_map(<unordered_map>)则是无序的,unordered_map内部实现了一个哈希表(也叫散列表,通过把关键码值映射到Hash表中一个位置来访问记录,查找的时间复杂度可达到O(1),其在海量数据处理中有着广泛应用)。
multimap(<map>) 容器保存的是有序的键/值对,但它可以保存重复的元素。multimap 中会出现具有相同键的元素序列,它们会被添加到容器中。

map、set、multiset、multimap这四种关联容器是有序的。有序的含义是它们按照顺序进行存储。

3.pair make_pair

std::pair主要的作用是将两个数据组合成一个数据,两个数据可以是同一类型或者不同类型。pair是将2个数据组合成一个数据,例如stl中的map就是将key,value放在一起来保存。当一个函数需要返回2个数据的时候,可以选择pair。 pair的实现是一个结构体,主要的两个成员变量是first,second 因为是使用struct不是class,所以可以直接使用pair的成员变量。

template pair make_pair(T1 a, T2 b)
{
    return pair(a, b);
} 

我们可以使用pair的构造函数也可以使用make_pair来生成我们需要的pair。 一般make_pair都使用在需要pair做参数的位置,可以直接调用make_pair生成pair对象。

vector<pair<int, int>> p(10,make_pair<int, int>(45, 99));
	cout << p[0].first << endl;//first得到pair的第一个值
	cout << p[9].second << endl;//second得到第二个值

4.四种智能指针

C++ 标准模板库 STL(Standard Template Library) 一共给我们提供了四种智能指针:(<memery>)auto_ptr、unique_ptr、shared_ptr 和 weak_ptr,其中 auto_ptr 是 C++98 提出的,C++11 已将其摒弃,并提出了 unique_ptr 替代 auto_ptr。auto_ptr的缺点是:存在潜在的内存崩溃问题。

unique_ptr

实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露。

shared_ptr

实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针。

weak_ptr

一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值