c++ STL六大组件之一 -- 容器 vector/deque/list/set/map/unordered_map 原理、用法详解

vector

​ vector 应该是最常用的容器了,vector 即动态数组, 与 string 类似,vector 成员物理上也是连续存放的,支持随机迭代器。

内存机制:

在这里插入图片描述

​ vector 可自动扩充内存, vector 自动扩容并不是每次超过扩充一个成员的内存,而是将容量扩充为原来容量大小的 2 倍,扩充后的容量为: vector.capacity() = vector.capacity() * 2。

​ 同时, vector 在扩容时会开辟一块新的较大内存,再将原有的成员复制到新的内存块,这会带来较大的性能。

vector<int> vec;
for(int i = 1; i < 10; ++i)
{
    vec.push_back(i);
		cout << "vec.size: " << vec.size() << ", vec.capacity: " << vec.capacity() << endl;
}

由下图所知:vector扩容大小为 2 的倍数。在这里插入图片描述

vector 常用 API

用法解释
reserve()当我们事先可以预估所需要存储的容量大小时可以首先调用 reserve() 预先分配内存,可以降低vector 扩容带来的性能损耗。
data()获取指向元素本身的指针
front()/back()返回 vector 中第一个/最后一个元素
begin()/end()返回指向第一个元素/最后一个元素的后一内存单元的迭代器
insert()/erase()可在 vector 任意位置插入/删除一个元素,参数:pos
push_back()/pop_back()插入一个元素在末尾/删除末尾元素
emplace()/emplace_back()构造成员对象,并插入所需插入位置,参数:pos, Args
resize()重新分配内存大小,同样是按照 2 的倍数进行分配内存。

​ 在使用 vector 常犯的错误就是没有考虑迭代器失效的问题。当我们删除元素节点时, 可能会触发底层的内存收缩操作,使得 vector 会另选一块较小的内存块,而先前使用的迭代器指向的内存块以及是原先分配的内存块,导致迭代器失效。如下例代码所示:


vector<int> vec {1, 2, 3};
	cout << "size: " << vec.size() << " capacity: "<< vec.capacity() << endl;
	for(vector<int>::iterator ite = vec.begin(); ite != vec.end(); )
	{
		// 1:错误版本
		//vec.erase(ite);
		//++ite;
		//2: 正确使用方式,注意 for循环中不需要执行 ++ite,否则会导致越界问题,有兴趣可以试试
		ite = vec.erase(ite);
	}
	cout << "size: " << vec.size() << " capacity: "<< vec.capacity() << endl;

使用第一种方法删除元素,导致迭代器失效,内存非法访问:
在这里插入图片描述

deque

​ deque 的意思就是 double - end queue,即双端队列。双端队列可以在队列的首端和未端任意的添加和删除元素。

​ deque 的接口与 vector 相比有以下不同:

​ deque 有 pop_front() 、push_front() 和 emplace_front() 接口用于在队列首端插入或删除元素;

​ deque 没有 data()、capacity() 和 reserve() 接口。

​ deque 内存布局并非完全连续,而是由固定大小的数组序列组成,一般如下:

在这里插入图片描述

list

​ list 即双向链表,内存布局不连续。可以以 o(1) 的复杂度在中间任意位置插入或删除元素节点,和 vector 相比, 无须大量移动元素节点。

在这里插入图片描述

​ list 较不同的接口如下:

​ merge():合并两个有序的 list,,示例如下:

std::list<int> list1 = { 5,10,2,4,3 };
std::list<int> list2 = { 9,7,2,3,5,4 };

list1.sort();
list2.sort();

list1.merge(list2);

​ splice(): 将元素从一个 list 转移到另一个 list,示例如下:

#include<iostream>
#include<list>
using namespace std;
ostream& operator<<(ostream& ost, list<int>& list)
{
    for(auto & i : list)
    {
        ost << i <<" ";
    }
    return ost;
}

int main()
{
    list<int> list1{1, 2 ,3, 4};
    list<int> list2{5, 6, 7, 8};
    cout << "list1: " << list1 << endl;
    cout << "list2: " << list2 << endl;
		//在 list1 的第一个位置迁移 list2 的前两个元素;
    list1.splice(list1.begin(), list2, list2.begin(), ++++list2.begin());
    cout << "after splice: " << endl;
    cout << "list1: " << list1 << endl;
    cout << "list2: " << list2 << endl;

    return 0;
}

set / unordered_set

​ set 和 map 是关联性容器,关联容器实现了可快速搜索的排序数据结构(复杂度O(log n))。而 vector、list 、deque、array属于序列容器,实现了可以顺序访问的数据结构。

​ set 和 map 底层是由红黑树实现,红黑树是一种近似平衡二叉树,可以以 o(log n ) 的复杂度实现快速查找,元素间有序;

unordered_set 和 unordered_map 底层是由哈希表实现(空间换时间),可以以o(1) 的复杂度实现快速查找,但是元素间无序。

​ set 不能存放重复的 value 的节点,如果需要存放重复的 value 节点 ,可以使用 multiset 和 unordered_multiset。

set 常用 API

begin() / end()返回指向第一个元素的迭代器 / 返回集合最后一个元素后面的元素的迭代器
insert(value) /erase()在集合任意位置插入一个元素节点,如果集合中已经拥有该key值的节点,将会插入失败
find(value)在集合中,查找对应的节点,并返回对应的迭代器
// insert   
set<int> setInt{1, 3, 5, 4};
pair<set<int>::iterator, bool > result_1 = setInt.insert(1);
cout << * result_1.first << ", " << (result_1.second ? "insert successful." : "insert unsuccessful") << endl;
pair<set<int>::iterator, bool > result_2 = setInt.insert(2);
cout << * result_2.first << ", " << (result_2.second ? "insert successful." : "insert unsuccessful") << endl;

在这里插入图片描述

//set 有序, unordered_set 无序
set<int> setInt{2, 4, 5, 1, 3};
cout << "setInt output: ";
for(auto & i : setInt)
{
    cout << i << " ";
}
cout << endl;
unordered_set<int> setInt2{2, 4, 5, 1, 3};
cout << "unordered_set  output: ";
for(auto & i : setInt2)
{
    cout << i << " ";
}
cout << endl;

在这里插入图片描述

multiset

cout << "multiset: ";
multiset<int> setMulti{1, 2, 1, 3, 5, 3};
for(auto & i : setMulti)
{
    cout << i << ", ";
}
cout << endl;

在这里插入图片描述

map / unordered_map

​ map 的底层实现以及接口和 set 类似。但是 map 属于字典类型, 需要存放 <key, value> 的键值对。同时 map 支持下标访问:map[key] = value。

// map 常见用法
struct compare
{
    bool operator()(const string& lhs, const string& rhs) const
    {
        return lhs.size() > rhs.size();
    }
};

int main()
{
    map<string, int, compare> Map{{"1", 1}, {"12", 2}, {"123", 3}};
    for(auto & ref : Map)
    {
        cout << " first: " << ref.first << " second: " << ref.second << endl;
    }
  	
  	//查找
    auto ite = Map.find("1");
    if(ite != Map.end())
    {
        cout << "first: " << ite->first << " second: " << ite->second << endl;
    } 
    return 0;
}

unordered_map 用法,自定义哈希函数:


struct Hash
{
    size_t operator()(const pair<int, int> & key) const
    {
        size_t h1 = std::hash<int> {}(key.first);
        size_t h2 = std::hash<int> {}(key.second);
        return h1 ^ (h2 << 1); // or use boost::hash_combine
    }
};

int main()
{
    unordered_map<pair<int, int>, int, Hash> Map{{make_pair(1, 2), 1}, 
                                                 {make_pair(1, 3), 1}, {make_pair(1, 4), 1}};
    
    for(auto & ite : Map)
    {
        cout << "key.first: " << ite.first.first << ", key.second: "
        << ite.first.second << ", value: " << ite.second << endl;
    }

    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值