9.算法与数据结构——C++STL

1.顺序容器

list列表 (基于双向链表,非连续)

某些STL中用的甚至是双向循环链表。既然是基于双向链表显然其不是连续存储的。
所以每个节点不光包括val 值,还有前后两个指针。
list包含了一个指针,重载了运算符,实现迭代器的递增递减与取值操作
list实际上是一个带有头节点的双向循环链表,node指针指向空的头节点
由此begin就是node->next的val
end 就是node本身,因为是循环的
empty判断node->next==node?即可
back就重载–,找到node的上一个节点。

注接下文array:
不确定大小/频繁插入删除用list
大量查找,用array

这样符合我们操作系统里的局部性原理。

array 固定数组,连续存储

用法:

array<种类,数目> name {具体数值初始化};

9.算法与数据结构——C++STL_第1张图片
因为array容器中新增了类似at()这样的成员函数,其会检查n是否有效,否则抛出异常,所以比普通数组安全。

vector 动态数组(连续空间,基于三个指针)

动态数组,用于 O(1) 的随机读取.
实现原理:
由三个指针实现,
第一个指针指向vector起始位置
第二个指针指向目前有数据的末尾
第三个指针指向整个vector容器所占内存的末尾。
9.算法与数据结构——C++STL_第2张图片
由此实现:

template<class _Ty,
    class _Ax>
    class vector
        : public _Vector_val<_Ty, _Ax>
    {   // varying size array of values
public:
    /********/
protected:
    pointer _Myfirst;   // pointer to beginning of array
    pointer _Mylast;    // pointer to current end of sequence
    pointer _Myend; // pointer to end of array
    };

由此:
注意:
end 指向第二个指针
size 是2-1 而capacity 是3-1
注意resize的时候,是新在堆中开辟内存,赋值之后删除旧数组

template <class _Ty, class _Alloc = allocator<_Ty>>
class vector{
public:
    iterator begin() {return _Myfirst;}
    iterator end() {return _Mylast;}
    size_type size() const {return size_type(end() - begin());}
    size_type capacity() const {return size_type(_Myend - begin());}
    bool empty() const {return begin() == end();}
    reference operator[] (size_type n) {return *(begin() + n);}
    reference front() { return *begin();}
    reference back() {return *(end()-1);}
    ...
};

详细使用见:
添加链接描述

deque 双端队列 数组挂载小段空间,连接非连续的空间

支持O(1)的随机读取和头部、尾部的频繁增删
9.算法与数据结构——C++STL_第3张图片
为了管理这些连续空间,deque 容器用数组(数组名假设为 map)存储着各个连续空间的首地址。也就是说,map 数组中存储的都是指针,指向那些真正用来存储数据的各个连续空间

当 deque 容器需要在头部或尾部增加存储空间时,它会申请一段新的连续空间,同时在 map 数组的开头或结尾添加指向该空间的指针,由此该空间就串接到了 deque 容器的头部或尾部。

遍历的时候四个指针,一个头first一个尾last,一个cur,最后一个node指向map数组指针,如果cur遍历到边缘,就用node指针来更新前三个指针。

由这四个指针,维护两个迭代器:一个start记录map数组中首个连续空间的信息,一个finish迭代器记录map数组最后一个连续空间的信息。但是start的cur指向连续空间的第一个数,而finish的cur指向连续空间的最后一个数。
9.算法与数据结构——C++STL_第4张图片

2.基于上述容器产生的数据结构

stack 栈(基于deque)

默认基于deque实现,后入先出LIFO。
常用于深度优先搜索,字符串匹配等问题。

堆和栈的区别

首先要了解内存分区:

  1. 栈区:由编译器分配释放,存储局部遍历和函数参数等。
  2. 堆区:由程序员分配释放,由malloc和free负责
  3. 自由存储区:可以是堆也可以是静态存储区(取决于operater new的细节实现方式);所以new和delete从自由存储区分配。
  4. 全局/静态区:全局遍历和静态变量。
  5. 常量存储区:存放常量,不允许修改

区别:

  1. 管理方式:堆由程序员控制,容易内存泄漏。而栈由编译器控制。
  2. 申请后相应方式:操作系统有一个记录空闲内存的链表,申请堆时遍历链表,寻找第一个大于需要空间的堆节点,删除该节点分配给程序。而栈只要剩下的空间够申请就一直提供内存,反之则栈溢出。
  3. 申请大小限制:堆是不连续的,理论上看虚拟内存多大就能多大。而栈是连续的,一般是一两M大小
  4. 效率:栈在计算机底层有专门寄存器提供服务,很快。相反堆需要库函数,比较慢。
  5. 碎片:堆频繁new和delete会造成大量碎片,降低程序效率
  6. 生长方向:堆向高地址方向增长,栈向低地址方向

queue 队列(基于deque)

默认基于deque,先入先出FIFO
常用于广度优先搜索

priority_queue(基于vector)

最大值先出的数据结构,默认基于vector实现堆结构。它可以在O(nlogn)的时间排序数组,O(logn) 的时间插入任意值,O(1) 的时间获得最大值,O(logn) 的时间删除最大值。priority_queue 常用于维护数据结构并快速获取最大或最小值。

3.有序数据结构

set 有序集

默认基于红黑树(一种特殊的二叉查找树)。元素不可重复
它可以在 O(nlogn) 的时间排序数组,O(logn) 的时间插入、删除、查找任意值,O(logn) 的时间获得最小或最大值。这里注意,set 和 priority_queue 都可以用于维护数据结构并快速获取最大最小值,但是它们的时间复杂度和功能略有区别,如priority_queue默认不支持删除任意值,而set获得最大或最小值的时间复杂度略高,具体使用哪个根据需求而定。

map 有序表

在set的基础上加上映射关系,使得每个key存一个value值。

4.无序数据结构(对每个有序容器作哈希处理)

unordered_set 哈希集

可以在 O(1) 的时间快速插入、查找、删除元素,常用于快速的查询一个元素是否在这个容器内
如果需要每次判断一个元素在不在一个vector内,我们可以将其转化成一个哈希集:这样就能很快的查找:

unordered_set<int> dict(v.begin(),v.end());
if(v.count(a))
{
...
}

unordered_map 哈希表

在 unordered_set 的基础上加上映射关系,可以对每一个元素 key 存一个值 value。在某些情况下,如果 key 的范围已知且较小,我们也可以用 vector 代替 unordered_map,用位置表示 key,用每个位置的值表示 value
详情见:
添加链接描述

你可能感兴趣的:(算法,数据结构,c++,stl)