PART1
1.int型整数的最大值和最小值分别是什么?如何用二进制表示?使用最大值和最小值进行计算时会出现什么问题?
2.类声明成指针相对于声明成对象有什么好处?
3.迭代器可以加减常数吗?
4.动态绑定是如何实现的?
5.哈希函数是什么?
6.当发生哈希表冲突时,有哪些处理方法?
7.deque的底层结构是什么?
8.哪些迭代器有除了!=和==之外的关系运算?
9.迭代器it进行it++和++it的区别
10.Lambda表达式用来做什么?语法形式是什么?捕获列表有几种形式?
11.线性表是什么?
13.mutable关键字的作用是什么?
14.编译过程的四个步骤是什么?
15.希尔排序是什么?时间复杂度是多少?
-
Lambda表达式的底层实现是什么?
-
vector是如何扩容的?
-
vector的resize和reserve方法有什么区别?
19.bitset类如何使用?
20.deque类如何使用?
21.仿函数是什么?
22.函数指针是什么?作用是什么?
PART2
1.int型整数的最大值和最小值分别是什么?如何用二进制表示?使用最大值和最小值进行计算时会出现什么问题?
INT_MAX: 2^31-1 01111111111111111111111111111111
INT_MIN:-2^31 10000000000000000000000000000000
会发生的问题:
INT_MAX +1 = INT_MIN
INT_MIN - 1 = INT_MAX
abs(INT_MIN) = INT_MIN
2.类声明成指针相对于声明成对象有什么好处?
多态特性
可以用new操作符来申请动态内存
生命周期:由程序员决定何时释放对象
对象的共享和传递:传参时不用拷贝整个对象,提高效率
3.迭代器可以加减常数吗?
只用存储在连续空间的容器的迭代器可以加减常数,因为这些容器支持随机访问。
vetcor, deque, string的迭代器可以加减常数。
map, set, list, forward_list的迭代器不可以加减常数,只能自增自减。
4.动态绑定是如何实现的?
父类指针或引用指向子类对象,父类指针调用虚函数,在运行时实现动态绑定,根据虚函数表,确定调用哪个类的该函数。
5.哈希函数是什么?
将一组数据映射到一段有限的地址空间的函数。
6.当发生哈希表冲突时,有哪些处理方法?
开放寻址法
- 线性探测法:若地址冲突,沿着该地址加一,直到找到空单元
- 平方探测法:若地址冲突,沿着该地址加1^2, 2^2, 3^2.... 直到找到空单元
- 双散列函数探查法:若地址冲突,沿着该地址加上一个步长,步长由关键字在第二个哈希函数中计算得到(若地址空间在0到n-1之间,那么第二个哈希函数计算出的值在1到n-1之间,且该值与n互素),直到找到空单元。该探查序列具有随机性。
- 伪随机探查法:建立一个伪随机数序列list,若地址冲突,关键字加上伪随机数序列中的值list[i],再次计算地址,直到地址不再冲突。
链地址法
将地址都为m的元素都存储在一个单向链表中,哈希表中的第m个单元指向该单向链表。
再哈希法
构造多个哈希函数,当第一个哈希函数产生地址冲突时,就使用第二个哈希函数,直到不再发生冲突。不易产生聚集,但是增加了计算时间。
建立公共溢出区
将哈希表分为公共表和溢出区,当发生地址冲突了,就存储到溢出区。
7.deque的底层结构是什么?
deque的底层结构是由一段一段连续的等长的存储空间组成的,每段存储空间之间不一定连续。
为了管理这些连续空间,deque用数组存储了每段存储空间的首地址,也就是指针。
8.哪些迭代器有除了!=和==之外的运算?
vector, string, queue
可以进行的运算:
iter1 += iter2
iter1 -= iter2
iter1 - iter2 获取两个迭代器之间的距离
> < >= <= 元素靠后的迭代器大于靠前的迭代器
9.迭代器it进行it++和++it的区别
++it返回的是引用
it++返回的是临时对象,开销大,每次自增都在创建和销毁对象
10.Lambda表达式用来做什么?语法形式是什么?捕获列表有几种形式?
Lambda表达式用于定义和创建匿名函数。
语法形式: [捕获列表](参数列表)mutable或exception异常->返回类型 {函数体}
捕获列表有多种形式:
[]:不捕获父作用域的任何遍量
[=]:用值传递的方式捕获父作用域的所有变量
[&]:用引用传递的方式捕获父作用域的所有变量
[=a,&]:用值传递的方式捕获父作用域的a变量,用引用传递的方式捕获父作用域的其他变量
[&a,=]:用引用传递的方式捕获父作用域的a变量,用值传递的方式捕获父作用域的其他变量
11.线性表是什么?
具有相同特性的数据元素组成的有限序列。
线性表包含顺序表(数组)和链表。
13.mutable关键字的作用是什么?
用于修饰成员变量,表示const成员函数可以修改该变量。
14.编译过程的四个步骤是什么?
预处理:替换预处理指令,展开宏定义
编译:完成词法分析、语法分析、语义分析等操作,将源代码编译成汇编指令
汇编:将汇编指令翻译成机器语言的目标文件,生成一个与机器硬件相关的目标文件
链接:将目标文件与所需的库文件链接,生成最终的可执行文件
15.希尔排序是什么?时间复杂度是多少?
希尔排序是缩小增量的插入排序。把下标按照一定的增量分组,对每组进行直接插入排序。随着增量减少,每组的元素越来越多,直到最后被分成一组。
时间复杂度:O(N^(2/3))
- Lambda表达式的底层实现是什么?
编译器实现Lambda表达式,分三步:
创建一个Lambda匿名类,实现构造函数,用Lambda表达式的函数体重载operator()
创建一个Lambda匿名类的对象
使用该对象调用operator()
- vector是如何扩容的?
为了避免每次插入都扩容,vector会在插入元素前,预估所需大小,提前将底层容量开辟好。
Windows: 按照当前容量的1.5倍扩容(可以充分利用之前用到过的内存块)
Linux: 按照当前容量的2倍扩容
- vector的resize和reserve方法有什么区别?
resize: 改变vector的size大小,并对元素初始化。有可能会影响capacity。
若resize要求的大小小于vector当前的size,则舍弃多余元素(逻辑上舍弃,物理上还能访问)。
若resize要求的大小大于vector当前的size,则新添加元素,并对元素默认初始化。
若resize要求的大小大于vector当前的capacity,则先扩容,再添加元素,并对元素默认初始化。
reserve: 改变vector的capacity大小,不影响size。
若reserve要求的大小大于vector当前的capacity,那么会重新分配一块连续的存储空间,将vector原有的数据copy过去,这时vector的capacity增大了,但size不变。
若reserve要求的大小小于vector当前的capacity,则不会产生任何影响。
19.bitset类如何使用?
bitset<N> 是二进制集合,必须指定模板参数N,表示bitset有几位。
初始大小4字节,大小每次两倍增长
bitset<1> : 4字节
bitset<32> : 4字节
bitset<33> : 8字节
bitset<65> : 16字节
20.deque类如何使用?
双端队列,可以在队列头和尾插入和删除元素。可以用下标遍历deque。
21.仿函数是什么?
仿函数又称函数对象,它并不是一个函数,但有类似函数的功能,可以像普通函数那样调用,传入参数,有返回值。
仿函数是实现了operator()的一个类,有自己的成员变量。
22.函数指针是什么?作用是什么?
函数指针指向一个函数在内存中的起始地址。
函数指针可以指向参数列表和返回值类型与它相同的函数:
函数指针的声明: (返回值类型)(*函数指针名)(参数列表)int(*p)(string s);
函数指针的作用是将函数作为参数传递: process(0,0,p);p("hello");
函数指针不支持算数运算:+1, ++, --