xx公司后端开发一面
(1)C++ vector和list的区别?
(2)TCP为什么2次握手不行,TCP4次挥手的过程
(3)执行main函数之前和之后做了哪些工作?
(4)你在写程序的时候如果程序出现了死循环你怎么找到这个死循环?
(5)B+树的时间复杂度?为什么使用B+作为数据库的索引?
(6)Linux命令知多少?
(7)银行转账系统中如果A转账完成之后系统出错,系统怎么解决?
(8)MySQL引擎是什么?
(9)编程题:给你一个有序整数数组,数组中的数可以是正数、负数、零,请实现一个函数,这个函数返回一个整数:返回这个数组所有数的平方值中有多少种不同的取值。
(1)C++ vector和list的区别?
vector是动态数组实现的,一说到动态那肯定是在堆上分配空间的。如果容量超出原先设定的值,会以2倍扩增。性能上:因为是数组实现的,所以访问起来肯定是O(1)时间内访问。
因为是vector,所以会经常有插入和删除的操作:
如果在结尾插入并且空间够的情况下,很快,如果空间不够,则首先要进行扩容,扩容的过程中完成内存拷贝。在中间拷贝也是一样,如果空间足够大,只需要完成插入位置后的元素拷贝就行了,如果内存不够则也需要先进行扩容,然后进行拷贝。
如果删除的是结尾的元素的话很快就可以完成,如果是中间的元素那就需要拷贝了。
总体而言由于vector的特性原因,所以它很适合随机访问,并且插入删除在结尾部。
list是双向链表实现的,由于是双向链表,所以肯定也是在堆上分配空间的。
那自然插入和删除都是很容易的,因为双向链表实现的原理就是为了插入和删除。
具体的区别和联系:
都是在堆上分配空间
vector是基于动态数组实现的,list基于双向链表实现的
vector不便于中间插入和删除,list支持随机插入和删除
(2)TCP三次握手的过程:
A:我要跟你建立连接
B:好的,我准备好了,我可以和你建立连接
A:那我们开始吧
第三次握手是为了防止发送的报文未能及时到达服务端引发的问题,如果只有两次握手则在这种情况下会建立两个连接。
四次挥手的过程:
A:我要跟你断开连接了
B:你等一下,我还有东西没处理完
B:我处理完了,可以断开连接了
A;我断开连接了
四次挥手的原因是TCP通信本身是全双工的,所以每一方都要确认断开连接,如果只有三次握手的话,那第三次握手完,服务器端就不知道客户端是否收到了第三次握手的消息,这也是为什么客户端在第三次握手后会等待一个2MSL时间的原因,防止第四次握手的消息服务器没收到,如果服务器端没收到第四次握手的消息,服务器就会以为客户端没有收到第三次握手的消息,这时,服务器会在此给客户端发送第三次握手的消息。
(3)执行main函数之前和之后做了哪些工作?
main函数执行之前主要是系统的初始化资源:
- 在栈区:设置栈指针
- 在data段:初始化全局变量和静态变量
- 在bss段:对未初始化的全局变量进行赋初值,bool是false,short,int,long 是0,指针是NULL
- 将main函数的参数传到main函数里面
main函数执行完成之后并不一定意味着进程结束。
main函数执行完成之后:
- 全局对象的析构函数会在main函数的执行后执行
- 使用atexit注册的函数会在main函数执行之后执行
void fn1(void) { printf("next.\n"); } void fn2(void) { printf("executed "); } void fn3(void) { printf("is "); } void fn4(void) { printf("This "); } int main(void) { // 注册需要在 main 函数结束后执行的函数. // 请注意它们的注册顺序和执行顺序 // 在 main 函数结束后被调用,调用顺序与注册顺序相反。 先注册后执行。 atexit(fn1); atexit(fn2); atexit(fn3); atexit(fn4); // 这条输出语句具有参照性,它可不是最后一句输出. puts("This is executed first."); // EXIT_SUCCESS 代表 0,它定义在 stdlib.h 中. // 我只是顺便提一下,也许你知道,但我担心你不知道,呵呵. return EXIT_SUCCESS; } 参考:https://blog.csdn.net/huang_xw/article/details/8542105
(4)你在写程序的时候如果程序出现了死循环你怎么找到这个死循环?
首先需要找可能出现死循环的进程,一个程序执行好久没有结果有可能有两个结果,第一:程序正在正常运行,但还没结果,第二:程序出现死循环
首先查看进程使用资源情况,如果内存占用正常,但是CPU占比接近100%,就说明可能出现死循环。
再使用pstack $pid查看进程栈,如果进程栈总是停留在一个位置,那这个位置就是死循环的位置,在文件里查看具体的代码就可以了。
(5)B+树的时间复杂度?为什么使用B+作为数据库的索引?
如果不考虑磁盘IO读取时间,M叉二叉树的时间复杂度是log(m,n);
因为B+树能够提供稳定高效的范围扫描,他的叶子节点是相互连接的,并且是排好序的,所以是为磁盘等外存设备设计的一种索引结构,而mysql的存储是存在外存上的。
B+树作为索引结构:还具有以下优点
- 只有叶子节点才记录数据,非叶子节点只包含索引;所有非叶子节点也就是内部节点都只保存叶子节点最小值作为索引,那么这种结构的设计意味着内存中索引节点越多,他的整个IO次数就会下降
(6)Linux命令不懂,没回答上来
(7)银行转账系统中如果A转账完成之后系统出错,系统怎么解决?
数据库回滚
(8)MySQL引擎是什么?
InnoDB、Memory、MyIsAm、Merge存储引擎。
(9)编程题:给你一个有序整数数组,数组中的数可以是正数、负数、零,请实现一个函数,这个函数返回一个整数:返回这个数组所有数的平方值中有多少种不同的取值:
思路:这个题其实是leetcode有序数组的平方变过来的https://leetcode-cn.com/problems/squares-of-a-sorted-array/
其实思路也很简单,首先找数组中平方和最大的数,然后找次大的,一直找直到遍历完数组;
int findNumOfSquare(vector<int> &nums) { if (nums.size() < 2) { return nums.size(); } int cnt = 0; int i = 0, j = nums.size() - 1; while (i <= j) { int num1 = abs(nums[i]); int num2 = abs(nums[j]); if (num1 == num2) {//当前最大平方数为num1*num1或者num2*num2 //去重 while (i <= j && abs(nums[i]) == num1) { i++; } while (i <= j && abs(nums[j] == num2)) { j--; } } else if (num1 > num2) {//当前最大平方数为num1*num1 //去重 while (i <= j && abs(nums[i]) == num1) { i++; } } else {//当前最大平方数为num2*num2 //去重 while (i <= j && abs(nums[j]) == num2) { j--; } } cnt++; } return cnt; }