好久好久么写了,看的书不少却很难落实记录下来,难得浮生半日闲,实验室项目暂告一段,最近阿里和中行面试终于基本完了,终于可以再捧起侯捷大神的STL,Mark一下,督促自己多书多记录,希望2019自己能在项目,毕设和找工作之间游刃有余。
目录
语法两则:
语法:
1、临时对象的产生—一种无名对象,不在程序预期之下(passer by value会产生临时对象,负担)
2、静态常量整数成员在class内部可直接初始化
线性容器:
空间配置器
Vector
list
deque:
stack:
queue
heap
priority_queue和functional 的oush_heap pop_heap
push_heap和priority_queue的基本操作代码
两个堆的应用示例:中位数和合并k个链表
中位数(牛客)
合并K个链表(力扣23)
有时刻意制造临时对象,程序更加清楚----在类型后面加()且可指定初值,int(8),shape(3,5)
仿函数中的应用:
Template
Class print
{
public:void operator()(const T&element) const
{
Cout< } } for_each(iv.begin(),iv.end(),print Class{ Static const int a = 10; Static const long b = 20; Static const char c = ‘c’; } SGI标准空间配置器 std::allocator 仅仅是对new和delete的包装而已;效率低,不用。 SGI STL 每个容器的缺省配置为alloc new ->operator new配置内存->构造函数 delete->调用析构函数->delete释放内存 内存配置器: 向heap申请空间, 考虑多线程状态, 考虑内存不足的应变措施, 考虑过多的小型区块可能造成的内存碎片问题 为了解决内存碎片问题alloc祭出双层配置器: 一级配置器:allocateà调用mallocà调用不成功调用oom_malloc Realloc->调用realloc->调用不成功调用oom_realloc 二级配置器:(好像有点类似于linux内存分配的伙伴系统) 小额区块导致内存碎片,且配置时需要额外负担(需要一部分内存交给系统进行管理,交税,内存越小越浪费)。小于128字节则以内存池管理,维护与之对应的自由链表(free_list) 。分配内存则从freelist中找,释放小额内存则回收。 内存池维护16个free_list 8 16 24……128字节,为了方便管理内存,将内存碎片调整到8的倍数。内存节点使用共用体,当内存没有分配时,节点填充有指向节点的指针,维持其在链表中的位置,当节点分配后,填充客户数据,降低指针对内存的浪费。 allocate: 分配空间大于128字节则调用一级配置器,否则检查对应的free_list,如果有则拿来用,没有则调用refill为free_list蓄水。 refill chunk_alloc 内存池: 当free_list没有内存时,refill在内存池取多个(默认20)的相应内存给链表,其底层调用chunk_alloc函数,chunk_alloc以end_free – start_free判断内存池水位,水量充足则调出20个内存单位给free_list,其中一个给用户,19个给free_list维护。如果不足,但够一个内存单元则分配数目以引用的参数返回,如果内存池不够一个内存单元则malloc从heap中配置,引入新的活水,新水量为需求量的2倍(40)+附加量(正相关配置次数)。 deallocate(): 该函数先判断区块是否大于128字节,是则交给一级配置器去回收,否则搜索对应的free_list进行回收。 Vector的迭代器就是普通的指针。 空间配置器:alloc 动态内存分配,扩容会以原来两倍的空间拷贝赋值,原空间释放,。连续空间,任何引起内存重新配置操作到会导致迭代器失效。 erase pop_back等删除函数只能改变size不能改变capacity 根据源代码很容易推断出vector的capacity只增不减。 clear ->调用erase也只是改变size Insert若空间不足会2倍扩容,迭代器失效。 底层是双向环状链表,一个指针即可完成所有操作,如下图,刻意增加一个空节点(尾后节点),便于头尾操作,其迭代器是封装节点类型的指针,重载* -> ++ --等操作符。配置器使用alloc作为配置器,封装了一个list_node_allocator便于节点的配置。由于链表的数据结构特性,除了操作的迭代器,其他迭代器不会失效。 List的成员函数操作: Insert->返回迭代器在插入哨兵元素迭代器之前。 push_back push_front调用insert erase->删除哨兵节点之后的节点 pop_front pop_back调用erase remove进行循环调用erase; clear->以尾后节点开始,尾后节点结束进行循环删除;最后置node为空节点。 Protected方法:transfer(position,first,last)将同类(一)链表的某一段移到链表的某个位置前面。 List.splice(iter,list2) 将list2拼接到iter之前à调用内部方法transfer List.merge(list2) 合并有序链表->在某一链表结束后transfer剩余节点 Reverse()->调用transfer Sort算法,由于STL算法里的sort必须支持Random Access Iterator所以只能用成员函数list.sort(); 允许双端插入,常数时间,vector头插效率奇差(大量移动);dequeue没有容量概念,提供随机访问,其迭代器设计比较复杂,运算效率差,如对于sort算法先拷贝到vector在sort在拷贝回来。能不用尽量少用。 Deque的数据结构:一个中控器存储指针,各个指针指向各个buffer(默认512字节)。即一个个连续的小空间被中控器连接起,相比于vector扩容避免了复制等操作高效,但是迭代器很复杂,内存维护也复杂。 迭代器:迭代器需要维护关于当前buffer的指针,first,last,cur和所在buffer在中控器中的位置node。 ++操作时,3判定cur==last,否则cur++,否则,set_node下一个节点,cur指向下一个缓冲头位置。--操作类似。 push_back和push_front: map会维护start和finish两个迭代器,表示node的范围,create_map_and_node进行deque结构安排时,会根据元素个数和缓冲区大小分配node个数,num_nodes num_elements/buffer_size() + 1,多分配一个node。map的start和finish也会放在map的中间,便于两边的插入。 当缓冲区不够时进行map扩容,map扩容不需要复制拷贝,在原来的基础上进行扩充,需要复杂的迭代器操作。 成员函数: pop_back():最后缓冲区有元素则cur指针前移,析构后面。没有则释放最后一个缓冲区,调整finish状态到前一个缓冲区最后一个位置,析构该元素。 pop_front():类似。 clear():将缓冲区析构释放掉,留一个缓冲区(初始状态)。 erase(position): 首先判断当前位置,if(index > size>>1)若在缓冲区前半段,将前面元素进行移位操作,将最前面冗余的元素去掉。否则后面的元素移动,去除最后的冗余元素。 Insert(position,value):在position之前插入value。若position.cur == start.cur即最前端则调用push_front,若在最后端则调用push_back。否则交给insert_aux操作,关于insert_aux也是首先判断index>size()/2,若前面元素少,则在最前端加入一个与第一元素相同的元素,然后向前逐个移动,否则在最后加一个,向后逐个移动。 适配器:默认以deque为底层数据结构,双向端口封闭头端,不能遍历,没有迭代器。 Stack 适配器:默认以deque为底层数据结构,不能遍历,没有迭代器。 Queue 二叉堆,完全二叉树,默认为大顶堆。以连续空间表述,父子关系为i/2 2i 2i+1;所以底层一般用vector进行实现(可动态改变大小且连续空间)。 算法里面有make_heap push_heap pop_heap sort_heap 等STL组件有priority_queue实现,入队以任何顺序进入,出队以权值大小出队。Heap不提供迭代器,不支持遍历。 push_heap(first,last)算法:末端加空节点,以50比较,向上浮动,浮不动填入即可。被调用时,新元素必须在底层vector的尾端,所以v.push_back() + push_heap(begin,end)。 pop_heap算法:堆顶置空,用68和子节点较大的比较,将空节点进行下沉,沉到底为止。 调用时弹出原素在底层vector的尾端,并未删除,所以pop_heap(begin,end)+v.pop_back()。 make_heap(first,last):将一段现有的数据转化为heap顺序。 sort_heap(first,last):将一个堆的数据进行排序(持续调用调用pop_heap将最大的放到vector最后面,弹完所有元素则vector已经排好序),破坏堆的特性。 priority_queue:优先级队列,默认Maxheap 底层封装vector+push_heap+pop_heap算法,适配器,没有迭代器,不能遍历。 priority_queue 运行结果: 题目描述: 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。 思路:维护两个堆,大顶堆和小顶堆,使得两个堆的大小相同; 代码: 运行结果: 题目描述: 合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。 示例: 思路:维护一个小顶堆,大小为k,现将k个链表的头节点入堆,进行出堆操作,堆顶出堆连接到新链表尾部,直到堆为空; 代码实现: 运行结果:2、静态常量整数成员在class内部可直接初始化
线性容器:
空间配置器
Vector
list
deque:
stack:
queue
heap
priority_queue和functional 的oush_heap pop_heap
push_heap和priority_queue的基本操作代码
#include"iostream"
#include"queue"
#include"functional"
using namespace std;
//使用方法 priority(Type,Conteiner,Function)
//std::priority_queue
两个堆的应用示例:中位数和合并k个链表
中位数(牛客)
class Solution {
public:
void Insert(int num)
{
if (((min.size() + max.size()) & 1) == 0)//目前总共偶数个数字
{
if (max.size()>0 && num
合并K个链表(力扣23)
输入:
[
1->4->5,
1->3->4,
2->]
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
struct greater_node
{
bool operator()(ListNode*a, ListNode*b)
{
return (a->val > b->val);
}
};
class Solution {
public:
ListNode* mergeKLists(vector