1vector底层实现
库函数的底层实现
vector类有三个迭代器start, finish和end_of_storage,分别代表目前使用内存空间的头和尾,以及目标可用空间的尾。
迭代器有点类似于指针
1
- 当我们在调用v.begin,v.end()时,返回的其实是start和finish迭代器,
- 当我们在调用v.begin,v.end()时,返回的其实是start和finish迭代器
而front和back返回的是迭代器里面的内容,其中back返回的*(end()-1)
2
当我们调用empty()时,返回的其实是begin()==end()
3
push_back和insert都是如果finish不等于end_of_srtorage就直接构造,并且finish++,否则就会申请更大的内存空间
4
例如v(10) ,10是指的它size() (也就是finish-start)。
resize(newsize,value):
- 若newsize 小于原来的size,则需要删除区间为[newsize, size]元素,end_of_storage指针不移动。例如newsize =5, 就会删除[5,9]
- 若newsize >=原来的size, 则再end()迭代器后面插入newsize - size个对象,并用value进行初始化,若插入增长大于end_of_storage,就会申请新的内存空间
[注]:若需要插入大量的数据在vector里,最好是如果能提前知道总的容量,用resize,或者直接创建的时候规定size,可以避免大量的申请内存的操作。
5 vector重载了运算符[]。在使用中括号时,例如v[i]返回的其实是* (begin() + i)
扩容
vector 使用的线性存储方式,可以动态扩容,VS2015是1.5倍扩容。动态扩容不是在原内存空间后面续接新空间,而是找更大的内存空间,然后将原来的数据拷贝到新空间并释放原空间。
具体流程:
1 在尾部插入新元素之前,需要检查是否还有空间,也就是看迭代器finish是否等于end_of_storage
2 若不等于,则说明还有空间,直接插
3 若等于则说明需要扩容,
- 首先会记录现在vector的size,此时也是它的容量,如果为0,则将大小扩容直1,否则扩容成原来的两倍,我记得好像也要看编译器的,VS2015是扩容到原来的1.5倍。
- 然后再将原来vector里的内容全部拷贝到新空间里,包括插入的元素
- 最后释放原来vector的内存空间,并调整迭代器,(start, finish和 end_of_storage)指向新的内存空间。
4 扩容是有极限的,我在往上看到其他博主测试过,超出那个极限后系统就拒绝插入元素了。
TOP K 问题
若是数据量很大,且重复数据很多,可以先用哈希表去重,然后再采用分治,找到每个组里的top k个数据,知道能够放进内存 ,然后用快排或者小顶堆或大顶堆。
map和unordered_map区别
map的底层是基于红黑树实现的,它是有序的,插入查找删除的时间复杂度都是logn , 缺点就是时间较慢
1@#$%~u nordered_map是基于哈希表实现的,内部是无序的,查找插入删除的时间复杂度都是o(1),缺点是哈希表的建立耗时长
哈希表底层
hash表的实现主要包括构造哈希和处理哈希冲突两个方面:
构造哈希有:直接地址法,除留余数
处理哈希冲突: 开放定址法、再哈希法
AVL树和红黑树
定义和特点:
- 平衡二叉树 :本质上是带平衡功能的二叉搜索树,左右子树都是平衡二叉树 ,且左右子树的高度之差不超过1
- 红黑树:一种弱平衡的二叉查找树,每个节点都是非红即黑,根节点和叶子节点都是黑的
区别:
-
AVL树是高度平衡的,频繁的插入和删除会引起频繁的reblance,导致效率降低,适用于不经常改变的情况
-
红黑树不是高度平衡的,插入最多两次旋转,删除最多三次旋转。适用于插入删除较多的情况,O(logn)
-
数据库《mysql技术内幕innodb存储引擎》 分布式数据库
-
数据结构,看STL的底层实现
-
各种树,以及他们在不同场景的应用,什么情况下选什么树,为什么,优缺点
-
多线程
-
计算机网络中HTTP,三个版本的区别,https加密技术
-
TCP三次握手,四次挥手,为什么
快排,并查集,连通域,滑动窗口,双指针,动态规划,链表