这次是阿里云2面,自我感觉面试表现很差,要凉。不过还是要抱着学习的态度查缺补漏。
这次的面试内容是先问计算机基础知识,后出了3道算法题。
计算机基础知识
- C++多态:为什么基类的析构函数要设置为虚函数?
当存在用基类指针操作派生类时,如果基类析构函数不定义为虚函数,那么delete该指针时,只调用基类的析构函数,而没有调用派生类的析构函数,造成派生类实例的资源没有被释放而内存泄漏。之所以这样是跟多态的静态绑定和动态绑定机制有关,一般有virtual的都是动态绑定,很明显没有虚析构函数的基类指针指向的析构函数编译期就被静态绑定为基类的析构函数了。
参考资料:
牛客-基类的虚析构函数(指出是因为继承机制,如果基类析构不定义为虚函数,delete将不释放指针指向的空间,造成内存泄漏)
C/C++基类的析构函数为什么必须定义为虚函数?
C++中的静态绑定和动态绑定(多态) - C++存在哪些内存泄漏的情况?有哪些检测方法?
其实上一题就是考内存泄漏的题。内存泄漏在C++中一般就是指new出一块内存却没有delete它,很多时候还会丢失指向这块堆上空间的指针,造成没法释放的局面。整理一下,就我以前碰到过的内存泄漏情况会有1.构造函数中使用了new,但析构函数没有对应的delete;2.new出了一个数组,但只delete指向这个数组的头元素的指针,链表也经常会出现这个问题,都是需要遍历的delete操作才行;3.还有就是上道题多态机制导致的内存泄漏;当然还有其他,但我目前遇到的还是这3个居多。检测方法,就百度百科的资料显示,静态的就是看编译器,动态的会有插装技术,但我没怎么用过,顶多是以前VS会有个内存资源占用动态图。
参考资料:
百度百科-内存泄漏
C++中内存泄漏的几种情况 - 多进程与多线程的理解?不同进程间通信的方式?
这个面试复盘1里有回答。后面的问题是因为我用QQ这种通信软件打比方,有客户端有服务端程序,其实是两个程序,然后前辈就。。。顺着问不同进程间通信的方式(真的不知道该不该说自己嘴jian,还是前辈们太腻害了),我其实进程通信这块只记得3种,一个是学习操作系统有个管道(计算机网络好像也有)的技术,第二个是MFC总有个消息队列技术,第三个便是这个网络之间Socket套接字技术。不过查阅资料会发现还有很多通信技术,常用的有信号、管道、消息队列、共享内存、信号量、套接字和信号等。
这里额外提一句,谷歌浏览器其实是多进程浏览器,我一直认为它是多线程(早期的浏览器是的),其实每个网页都是一个进程,进程之间数据指令都是隔离的,也算是为了安全考虑!!!
参考资料:
多线程和多进程 及其应用场景
浏览器 线程?进程?
进程间通信的方式——信号、管道、消息队列、共享内存 - 计算机网络:三次握手?泛洪攻击的原理?
面试1有回答过。不过这里前辈在我回答为什么会三次握手的时候强行给我扭到了网络攻击???泛洪攻击是伪造IP向服务器发送大量的SYN报文,也就是三次握手第一次,服务器一旦响应,此次连接就会转化为半连接状态,其实也是在一个队列里,会为这些请求分配资源比如要发送ACK报文,甚至还会重发,可以想象一旦这样的恶意报文多了,服务器自身的资源会被消耗完, 其实这种无效的SYN报文多了,也会排挤掉那些正常的连接请求。
SYN泛洪攻击
算法题
- 判断数组是否是BST(二叉排序树)的后序遍历结果
输入 :4,3,5,8,10,9,7 返回 true
输入:4,9,5,8,10,3,7 返回 false
这道题当场没有做出来,当时第一反应是先手工复现这颗二叉树,但没有成功。现在再看,很明显BST树的中序遍历是由小到大,这就是说把这个输入数组升序排序,就是BST的中序遍历,再加上后序遍历,其实转化为问存不存在这样的树满足这样的中序与后序遍历顺序!!!因为中序与后序能够唯一对应一棵二叉树。
这里其实可以延伸一个知识点,为什么先序与后序不能唯一对应一颗二叉树呢,如何证明? 有个有意思的结论,如果二叉树中只有度为0或2的结点,其实用先序与后序遍历的结果也能唯一确定一颗二叉树,加了度为1的结点就不行了。
参考资料:
先序遍历和后序遍历为什么不能唯一地确定一棵二叉树?
leetcode-前序和中序确定二叉树
leetcode-后序和中序确定二叉树
下面是我整理后的这道题的代码:
# Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def isValid(self, post_arr) -> TreeNode:
def helper(in_left, in_right):
# if there is no elements to construct subtrees
if in_left > in_right:
return True
# pick up the last element as a root
if not postorder: #只要是错误的,构造过程便会出现两个序列剩余个数不一致的情况
return False
val = postorder.pop()
index = idx_map[val]
return helper(index + 1, in_right) and helper(in_left, index - 1)
# build a hashmap value -> its index
postorder = post_arr.copy()
post_arr.sort()
inorder = post_arr
idx_map = {val: idx for idx, val in enumerate(inorder)}
return helper(0, len(inorder) - 1)
if __name__ == '__main__':
post = [4,9,5,8,10,3,7]
solution = Solution()
result = solution.isValid(post)
print(result)
- 给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
这道题用栈模拟一遍即可。 - 一个楼梯有N阶,每此只能走1阶、2阶、3阶 有多少种走法?
这道题面试复盘1回答过,不过注意一点,我写的是带备忘录的递归,其实可以写成动态规划,这也是面试官想要的结果。
结语
第二位前辈感觉也是蛮好的,只可惜自己这几天身体状态不佳且备考方向错了,一直以为算法岗二面应该问的是机器学习的基础知识,要不是前面面试复盘1中总结复习了几个知识点,怕是会非常尴尬。还有就是算法题,做得越快越优,越能体现自己的编程水平,有志从事IT技术岗的我觉得无论是在校还是工作,应该要把刷题竞赛当作一项爱好坚持下去。
如果觉得不错,请素质三连哈~~