文章目录
一、自我介绍
个人背景、项目经历、实习经历。
二、Java后台
2.1 操作系统
2.1.1 应用程序执行过程
在Windows中,当启动一个应用程序时,系统调用CreateFile函数
打开磁盘上的.exe文件
,然后系统调用createFileMapping函数
,创建一个文件映射对象,最后系统代表新创建的进程调用MapViewOfFileEx函数
,使.exe文件
映射到进程的地址空间。然后系统创建该进程的主线程,将该映射试图的可执行代码的第一个字节的地址放入线程的指令指针,然后CPU启动该代码的运行。
2.1.2 C语言编译执行过程
- 第一阶段:预处理
在正式的编译阶段之前进行。预处理阶段将根据已放置在文件中的预处理指令来修改源文件的内容。如#include指令就是一个预处理指令,它把头文件的内容添加到.cpp文件中。 - 第二阶段:编译优化
将其翻译成等价的中间代码表示或汇编代码,并执行优化。 - 第三阶段:汇编
把汇编语言代码翻译成目标机器指令。 - 第四阶段:链接
例如某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
2.1.3 编译过程中出现未定义变量的原因
一般是因为变量、函数未声明或者头文件没包含进去。
2.2 计算机网络
2.2.1 http请求全过程
- 第一步:浏览器生成http请求信息(会话层)
(1)分解url
(2)生成http请求消息
(3)域名解析
(4)委托操作系统发送http请求 - 第二步:TCP模块发送请求(传输层)
(1)创建套接字
(2)连接服务器
(3)发送数据 - 第三步:IP模块发送请求(网络层)
(1)生成IP头部
(2)生成MAC头部 - 第四步:MAC模块发送请求(数据链路层)
(1)生成报头
(2)生成校验序列
(3)生成电信号 - 第五步:PHY模块发送请求(物理层)
最终网卡中的PHY模块会将通用电信号转换成网络传输所需的格式,通过网线发送出去。经过网络转发后,最终到达服务器。
2.2.2 hosts文件的作用
将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应网页,如果没有找到,则系统会再将网址提交DNS域名解析服务器进行IP地址的解析。
2.2.3 DNS使用TCP还是UDP
- 区域传送时使用TCP:
1)辅域名服务器会定时(一般时3小时)向主域名服务器进行查询以便了解数据是否有变动。如有变动,则会执行一次区域传送,进行数据同步。区域传送将使用TCP而不是UDP,因为数据同步传送的数据量比一个请求和应答的数据量要多得多。
2)TCP是一种可靠的连接,保证了数据的准确性。 - 域名解析时使用UDP:
客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。不用经过TCP三次握手,这样DNS服务器负载更低,响应更快。虽然从理论上说,客户端也可以指定向DNS服务器查询的时候使用TCP,但事实上,很多DNS服务器进行配置的时候,仅支持UDP查询包。
2.2.4 TCP和UDP可以同时使用相同的端口吗
可以,TCP和UDP在IP包头信息不同,可以被系统区分。
2.2.5 server和client通信过程中server挂掉会怎么样
三、算法题(手撕)
3.1 判断一棵二叉树为完全二叉树
采用层次遍历的方式,在判断一个节点的时候,如下的判断依据:
- 如果这个节点的左子树为null,右子树不为null,则一定不是完全二叉树。
- 如果这个节点的左右子树均为null,或者这个节点的左子树不为null但是右子树为null,则当前层或者下一层不能再出现含有左右子树的节点。
- 如果当前节点的左右子树均不为null,则观察下一个节点。
private static boolean isCompleteTree(Node node) {
if (node == null) {
return false;
}
boolean hasLeaf = false;
Queue<Node> queue = new ArrayDeque<>();
queue.add(node);
while (!queue.isEmpty()) {
Node tmp = queue.poll();
if (tmp.left() == null) {
if (tmp.right() != null) {
return false;
}
if (tmp.right() == null) {
hasLeaf = true;
}
} else {
if (hasLeaf) {
return false;
}
if (tmp.right() == null) {
hasLeaf = true;
queue.add(tmp.left());
}
if (tmp.right() != null) {
queue.add(tmp.left());
queue.add(tmp.right());
}
}
}
return true;
}
3.2 找到数组的拐点
采用二分查找,拐点元素既要大于左侧元素,也要大于右侧元素,即nums[i] > nums[i - 1] && nums[i] > nums[i + 1]
。
如果查找到的元素只比右侧大,则拐点在左半部分;如果查找到的元素只比左侧大,则拐点在右半部分。
private int findPeak(int[] nums) {
if (nums != null && nums.length > 0) {
if (nums.length == 1) {
return 0;
}
if (nums[0] > nums[1]) {
return 0;
}
int index = nums.length - 1;
if (nums[index] > nums[index - 1]) {
return index;
}
int i = 0, j = index;
int mid = 0;
while (i < j) {
mid = (i + j) / 2;
if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1]) {
return mid;
} else if (nums[mid] > nums[mid + 1]) {
j = mid - 1;
} else if (nums[mid] > nums[mid - 1]) {
i = mid + 1;
}
}
}
return -1;
}
四、算法题(口述)
4.1 走迷宫的走法总数(动态规划)
4.2 分治法与动态规划的区别
- 分治法:将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解而得到原问题的解。
- 动态规划:适用于子问题独立且重叠的情况,也就是各子问题包含公共的子子问题。
在子问题独立且重叠的情况下,若用分治法则会做许多不必要的工作,即重复地求解公共的子问题。动态规划算法对每个子子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到子问题时重新计算答案。