2021年1月5号面试字节跳动客户端,约50分钟。面完之后10分钟就进行二面了。
文末有福利噢!
主题一:C++
t1. 有没有了解过拷贝构造函数?
-
定义:拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。
-
作用:用来复制对象,使用这个对象的实例来初始化这个对象的一个新的实例。
-
调用时机:
- 当函数的参数为类的对象时
- 函数的返回值是类的对象
- 对象需要通过另外一个对象进行初始化。
-
拷贝构造函数默认为浅拷贝。浅拷贝是指当出现类的等号赋值时,它能够完成静态成员的值复制。当数据成员中没有指针时,浅拷贝是可行的。但当数据成员中有指针时,如果采用简单的浅拷贝,则两个类中的指针将指向同一个地址,对象即将结束时,两个类会分别调用析构函数,导致指针悬挂现象。所以这时必须采用深拷贝,在堆内存中另外申请空间来储存数据,防止指针悬挂现象。
-
拷贝构造函数必须是引用传递,不能是值传递,这是为了防止递归引用。
t2. 说一下C++里的智能指针:
-
C++里面的四个智能指针,auto_ptr,unique_ptr,shared_ptr,weak_ptr,其中后三个是c++11支持,并且第一个已经被c++11弃用。
-
使用原因:智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针是一个类,当超出了类的实例对象的作用域时,会自动调用对象的析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。
-
auto_ptr:采用所有权模式。p2剥夺了p1的所有权,但是当程序运行时访问p1将会报错。所以auto_ptr的缺点是:存在潜在的内存崩溃问题。
-
unique_ptr:实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露(例如“以new创建对象后因为发生异常而忘记调用delete”)特别有用,可以通过标准库的move()函数实现指针转移。
-
shared_ptr:实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。
-
weak_ptr:是一种不控制对象生命周期的智能指针, weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。
t3. C++中如何实现HashMap?链地址法的时间复杂度:
-
如何避免哈希碰撞:
(1)开放地址法:开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入
(2)再哈希法:当哈希地址发生冲突用其他的函数计算另一个哈希函数地址,直到冲突不在产生为止
(3)链地址法:将哈希表的每个单元作为链表的头结点,所有哈希地址为 i 的元素构成一个同义词链表。即发生冲突时就把该关键字链在以该单元为头节点的链表的尾部
(4)建立公共溢出区:将哈希表分为基本表和溢出表两部分,发生冲突的元素都放在溢出表中
-
不管插入还是查找,由key获取hash值然后定位到桶的时间复杂度都是 O ( 1 ) O(1) O(1) ,那么真正决定时间复杂度的实际上是桶里面链表/红黑树的情况。如果桶里面没有元素,那么直接将元素插入或者直接返回未查找到,时间复杂度就是 O ( 1 ) O(1) O(1),如果里面有元素,那么就沿着链表进行遍历,时间复杂度就是 O ( n ) O(n) O(n),链表越短时间复杂度越低,如果是红黑树的话那就是 O ( l g n ) O(lgn) O(lgn)。
-
因此HashMap的查找时间复杂度只有在最理想的情况下才会为 O ( 1 ) O(1)