盘点2020腾讯秋招C++后台面试题集

1、TCP三次握手的过程, accept发生在三次握手哪个阶段?

accept发生在三次握手之后。
第一次握手:客户端发送syn包(syn=j)到服务器。
第二次握手:服务器收到syn包,必须确认客户的sY(ack=j+1),同时自己也发送一个ASK包(ask=k)。
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1)。
握手完成后,客户端和服务器就建立了tcp连接。这时可以调用 accept函数获得此连接。

2、用UDP协议道讯时怎样得知目标机是否获得了数据包 ?

可以在每个数据包中插入一个唯一的ID,比如 timestamp或者递增的int。
发送方在发送数据时将此ID和发送时间记录在本地。
接收方在收到数据后将ID再发给发送方作为回应。
发送方如果收到回应,则知道接收方已经收到相应的数据包;如果在指定时间内没有收到回应,则数据包可能丢失,需要重复上面的过程重新发送一次,直到确定对方收到。

3、腾讯服务器每秒有2W个QQ号同时上线,找出5min内重新登入的qq号并打印出来。

如果空间足够大,可以定义一个大的数组a[qq号],初始为零然后.这个qq号登陆了
就a[qq号]++
最后统计大于等于2的QQ号
这个用空间来代替时间
不成熟的想法
2w x 300s
所以用6000.000个桶。刪除超时的算法后面说,所以平均桶的大小是1
假设qq号码一共有1010个,所以每个桶装的q号码是1010/(6*10~6)个,
这个是插入时候的最坏效率(插入同个桶的时候是顺序查找插入位置的)
qq的节点结构和上面大家讨论的基本一样,增加一个指针指
向输出列表,后面说

struct QQstruct {
    
    
num_type qqnum,
timestamp last_logon_time,
QQstruct *pre,
QQstruct *next,
OutPutlist *out //用于free节点的时候,顺便更新下输出列表
}
另外

增加两个指针列表
第一个大小300的循环链表,自带一个指向 QQStruct的域,循环存300秒内的qq指针。
时间一过就fee掉,所以保证所有桶占用的空闾在2wX30以内.
第二个是输出列表,就是存放题目需要输出的节点。
如果登陆的用户,5分钟内完全没有重复的话,每秒free2w个节点
不过在free的时候,要判断一下时间是不是真的超时,因为把节点入桶的时候,遇到重复的,
会更新一下最后登陆的时间。当然啦,这个时候,要把这个q号码放到需要输出的列表里面

4、请描述C++的内存管理方式.

在c++中内存主要分为5个存储区:
栈(Stack):局部变量,函数参数等存储在该区,由编译器自动分配和释放.栈属于计算机系统的数据结构,进栈出栈有相应的计算机指令支持,而且分配专门的寄存器存储栈的地址,效率分高,内存空间是连续的,但栈的内存空间有限。
堆(Heap):需要程序员手动分配和释放(new,delete),属于动态分配方式。内存空间几乎没有限制,内存空间不连续,因此会产生内存碎片。操作系统有一个记录空间内存的链表,当收到内存申请时遍历链表,找到第一个空间大于申请空间的堆节点,将该节点分配给程序,并将该节点从链表中删除。一般,系统会在该内存空间的首地址处记录本次分配的内存大小,用于delete释放该内存空间。
全局/静态存储区:全局变量,静态变量分配到该区,到程序结束时自动释放,包括DATA段(全局初始化区)与BSS段(全局未初始化段)。其中,初始化的全局变量和静态变量存放在DATA段,未初始化的全局变量和静态变量存放在BSS段。BSS段特点:在程序执行前BSS段自动清零,所以未初始化的全局变量和静态变量在程序执行前已经成为0.
文字常量区:存放常量,而且不允许修改。程序结束后由系统释放。
程序代码区:存放程序的二进制代码
在这里插入图片描述
在这里插入图片描述

更多一线互联网大厂面试资料以及视频资料,关注VX公众号零声学院免费领取

5、有序链表合并.

/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
    
    
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    
    
if (l1 == NULL) {
    
    
return l2;
} else if (l2 == NULL) {
    
     
return l1;
} else {
    
    
if (l1->val <= l2->val) {
    
    
l1->next = mergeTwoLists(l1->next, l2);
return l1;
} else {
    
    
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
}
};

6、hash表的实现,包括STL中的哈希桶长度常数。

hash表的实现主要涉及两个问题:散列函数和碰撞处理。
1)hash function (散列函数)。最常见的散列函数:f(x) = x % TableSize .
2)碰撞问题(不同元素的散列值相同)。解决碰撞问题的方法有许多种,包括线性探测、二次探测、开链等做法。SGL版本使用开链法,使用一个链表保持相同散列值的元素。
虽然开链法并不要求表格大小必须为质数,但SGI STL仍然以质数来设计表格大小,并且将28个质数(逐渐呈现大约两倍的关系)计算好,以备随时访问,同时提供一个函数,用来查询在这28个质数之中,“最接近某数并大于某数”的质数。

7、 hash表如何rehash,怎么处理其中保存的资源.

先想想为什么需要rehash:
因为,当loadFactor(负载因子)<=1时,hash表查找的期望复杂度为O(1). 因此,每次往hash表中添加元素时,我们必须保证是在loadFactor <1的情况下,才能够添加。
模仿C++的vector扩容方式,Hash表中每次发现loadFactor==1时,就开辟一个原来桶数组的两倍空间(称为新桶数组),然后把原来的桶数组中元素全部转移过来到新的桶数组中。注意这里转移是需要元素一个个重新哈希到新桶中的。

8、 redis的主从复制怎么做的?

Redis旧版复制功能只有同步和命令传播。新版复制功能加入了部分同步的功能。
1)同步:
2)命令传播:
当主服务器会将自己执行的写命令,也即是造成主从服务器不一致的那条写命令,发送给从服务器执行,当从服务器执行了相同的写命令之后,主从服务器将再次回到一致状态。
3)部分同步:(断线后重复制)
复制偏移量:通过对比主从服务器的复制偏移量,程序可以很容易地知道主从服务器是否处于一致状态。
复制积压缓冲区:主服务保存最近的写命令到复制积压缓冲区,是一个先进先出队列
服务器运行ID:从服务器记录上次同步的主服务器的Id。

9. ubuntu开机的时候系统做了什么?

  1. 加载BIOS
    BIOS程序首先检查,计算机硬件能否满足运行的基本条件,这叫做”硬件自检”。硬件自检完成后,BIOS把控制权转交给下一阶段的启动程序。
  2. 读取MBR
    计算机读取该设备的第一个扇区,也就是读取最前面的512个字节。如果这512个字节的最后两个字节是0x55和0xAA,表明这个设备可以用于启动;如果不是,表明设备不能用于启动,控制权于是被转交给”启动顺序”中的下一个设备。
  3. Bootloader
    在这种情况下,计算机读取”主引导记录”前面446字节的机器码之后,不再把控制权转交给某一个分区,而是运行事先安装的”启动管理器”(boot loader),由用户选择启动哪一个操作系统。
    Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核做好一切准备。
    Boot Loader有若干种,其中Grub、Lilo和spfdisk是常见的Loader。Linux环境中,目前最流行的启动管理器是Grub。
  4. 加载内核
    内核的加载,内核加载后,接开始操作系统初始化,根据进程的优先级启动进程。

10、程序什么时候应该使用线程,什么时候单线程效率高

1 耗时的操作使用线程,提高应用程序响应
2 并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求。
3 多CPU系统中,使用线程提高CPU利用率
4 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
其他情况都使用单线程。

猜你喜欢

转载自blog.csdn.net/lingshengxueyuan/article/details/108208100