字节跳动-广州-效率工程-2021届后端实习生

写在前面

1月14号牛客网内推投的简历,1月16号收到的面试邀请,1月17号下午三点的面试,1月17号下午五点四十的拒信。

HR面试邀约

  • HR在16号中午的时候加了我的微信,告诉我上午打电话没打通,我看了通话记录发现我上午在高速上开车太专心没听见Orz。
  • 在投递简历之后尽力保证自己的电话随时待命,不要给HR带来麻烦。

视频面前的硬件准备

  • 面试前通过了系统检测(耳机、麦克风、摄像头、网络),结果面试开始的时候家里网络崩了Orz,换成了手机热点,然后耳机又崩了Orz,重连了耳机,终于可以正常面试了。
  • 面试前一定要保证硬件环境的稳定。

面试经历

自我介绍

  • 自我介绍主要叙述了学校专业,实习经历以及项目经历。
  • 自我介绍的时候网络信号不好,所以面试官估计没有听清太多Orz。

项目切入 / Redis / MySQL

面试官从项目切入,主要了解了 Redis 红包池。

项目 Redis 中使用的数据结构是什么?

  • 因为小公司项目 Redis 用的是 String 数据结构。
  • 面试官其实想考察的是 Redis 的数据结构,这个时候就应该主动告诉面试官自己知道 Redis 的数据结构!

为什么手动淘汰 Redis 中的数据,不使用 Redis 的内存淘汰机制?

  • 因为 Redis 的内存淘汰机制是对 Key 值进行筛选,而红包池的机制是对 Value 值的筛选。
  • 面试官提及了内存淘汰机制,应该主动告诉面试官自己知道 Redis 的内存淘汰机制!

同时对 Redis和数据库进行内存更新的时候,如何保证操作的成功 / Redis更新后线程挂掉了怎么办?

  • 这题当时没听懂问的是哪个考察点,面试官很贴心的跳过了这个点。事后想了下,还是没有明白这个问题应该怎么回答。

数据库用的是什么?说说数据库事务的特性。

  • 数据库用的是MySQL。
  • ACID。
    • 原子性:一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
    • 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。这个当时没有答出来。
    • 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。这个当时没有答出来。
    • 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

计算机网络 / DNS / HTTP

了解 HTTP 协议吗?

  • HTTP 是超文本传输协议,基于 TCP 协议。

HTTP 协议的连接过程?

  • 请求 TCP 连接
    首先,浏览器与 Web 服务器的 HTTP 80端口建立一个 TCP 套接字连接。

  • 发送 HTTP 请求

    通过 TCP 连接,浏览器向服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据组成。

  • 接受请求返回响应

    Web服务器解析请求,定位请求资源。服务器将资源复本写到 TCP 套接字,由浏览器读取。一个响应由状态行、响应头部、空行和响应数据组成。

  • 释放TCP连接

    若 connection 模式为 close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放 TCP 连接

    若connection 模式为 keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求。

  • 解析 HTML 内容

    浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。

  • 当时问到这个回答的很简略,一直准备了 HTTPS 的回答,没想到忽略了基础的 HTTP 协议。

HTTP 协议的头部有哪些属性?

  • 通用信息头
    • Request URL 请求的地址 域名
      Request Method 请求的方法类型 GET / POST
      Status Code 响应状态码 200 OK / 404 NOT-FOUND
      Remote Address 表示远程服务器地址 IP地址
  • 响应头
    • Content-Length 响应体的长度
      Content-type 返回的响应MIME类型与编码:告诉浏览器它发送的数据属于什么文件类型
      Cache-control 指定请求和响应遵循的缓存机制
      • public 响应可被任何缓存区缓存
        private 对于单个用户的整个或部分响应消息,不能被共享缓存处理
        no-cache 表示请求或响应消息不能缓存
  • 请求头
    • Accept 告诉服务器可以接受的文件格式。
      Accept-Encoding gzip等指定浏览器可以支持的web服务器返回的内容压缩编码类型。
      Accept-Language 浏览器支持的语言。
      Cache-Control 指定请求和响应遵循的缓存机制。
      Connection keep-alive 表示是否需要持久连接。
      Cookie HTTP请求发送时,会把保存在该请求域名下的所有 cookie 值一起发送给web服务器。
      Host 指定请求的服务器的域名和端口号。
      Referer 告诉服务器是从哪个网站链接过来的。
      User-Agent 简称UA。内容包含发出请求的用户信息。
      Authorization 当客户端访问受口令保护时,服务器端会发送401状态码和 www-authenticate 响应头,要求客户机使用 Authorization 来应答。
  • 详细属性见文后附表。
  • 一开始答的时候我竟然报了半天状态码还浑然不知,然后看见面试官一脸诧异的看着我才反应过来Orz。重新听了问题后还是没有答出来Orz。

DNS 是什么?DNS 的查询过程?

  • DNS 是域名系统,通常用来解析域名为 IP 地址。
    本地解析 通过本地缓存进行解析。
    直接解析 向客户机所设定的局部 DNS 服务器发一个查询请求。
    递归解析 局部 DNS 服务器向该域名的根域服务器查询,再由根域名服务器一级级向下查询
    迭代解析 局部 DNS 服务器把请求转发至上一级 DNS 服务器,再请求上上级直到查询到该域名。
  • 当时只回答了递归解析和迭代解析,其实还有 Host 文件、本地和直接解析。

Golang

因为项目经历中使用了 Golang,所以面试官提问了 Golang 的基础问题。

Golang 的默认参数传递方式以及哪些是引用传递?

  • 默认采用值传递,且Go 中函数传参仅有值传递一种方式。
  • slice、map、channel 都是引用类型。
  • slice 能够通过函数传参后,修改对应的数组值,因为 slice 内部保存了引用数组的指针,并不是因为引用传递。
  • 这题回答的时候以为有引用传递,答了 slice、map、channel 都是引用传递,结果一百度,发现是用起来像引用传递,其实都是值传递,就像 slice 传递的是指针的复制。

Golang 的协程与 Java 线程的区别?

  • 协程是轻量级线程,多个协程可以由一个或多个线程管理。
  • 协程无需上下文切换,没有线程之间切换的开销。
  • 协程的调度不需要多线程的锁机制,因为只有一个线程,不存在同时写变量冲突,执行效率比多线程高很多。
  • 这题面试的时候没有答到第三点。

Golang 的协程间通讯方式有哪些?

  • 共享内存和协程通信。
  • 「Don’t communicate by sharing memory, share memory by communicating」所以更提倡使用 channel 进行通信。
  • 当时答的时候也只答了 channel,面试官说还有一种,怎么都想不起来Orz。

手撕代码

最后一个环节当然是面试者喜闻乐见的手撕代码环节,也是字节面试的传统。

仍然记得字节的面试通知邮件里写着如下内容:

如果有编码的要求,那注意我们需要的是可执行的代码,而不是伪代码。我们面试官普遍都有代码洁癖,良好的代码风格有助于提升你的竞争力。

如何判断一棵树是否为 BST ?

当时写的时候用的递归解法,然而万万没想到理解成了判断 AVL 树,一时间不知怎么下手。经过交流才知道 BST 不需要是一颗平衡树,面试官笑笑说你既然提到了 AVL 那就解释一下什么叫二叉树的平衡,当时回答了一棵树任意节点它的左右两个子树的高度差的绝对值不超过1,面试官说回答对了一半,回来查了之后才知道还有一种情况是左右子树都为空Orz,我果然还是太菜了。

然后写代码,因为没有提前写出二叉树的数据结构,结果代码里竟然出现了链表的属性(tmp.next)Orz,然后发现自己二叉树和链表混用了。蜜汁尴尬,然后第一次 Run 的时候还发现有把 right 写成 rigth 的地方,真的是太紧张了,代码漏洞百出。

远程面试使用的是牛客网,线上IDE只有一个默认的 main 函数,然而面试官在给出题目的时候不像平时做题时给出一个要求的函数签名,所以我们要先写出树的数据结构,然后写函数(面试官有说入参是一个根节点),最后测试的时候要自己 new 一棵二叉树,虽然没花多少时间,但是第一次面试还是不太习惯Orz。

因为没有提前刷题,代码这里卡了半个小时还没解对,不好意思再浪费面试官的时间,只好表达歉意说自己准备不充分,面试官先是表扬了前面回答的很流畅,然后说继续加油,再见。于是我就知道我终于可以加入字节的公司人才库了。

话不多说上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//二叉树结构
public class TreeNode{
TreeNode left;
TreeNode right;
int val;

public TreeNode(int val){
this.val = val;
}
}

//函数实现
public boolean isBST(TreeNode root,int min,int max){
if(root == null) return true;
if(root.val > min && root.val < max){
return (isBST(root.left,min,root.val)&&isBST(root.right,root.val,max));
}else return false;
}

//注意如下方法无法解决特殊情况
public boolean isBST(TreeNode root){
if(root == null) return true;
if(root.val > root.left.val && root.val < root.right.val){
return (isBST(root.left)&&isBST(root.right));
}else return false;
}

/**
* 注意特殊情况!
*
* 3
* / \
* 2 5
* / \
* 1 4
*
* 这不是一颗 BST 但是符合所有子树左子节点小于根节点,右子节点答于根节点。
*/

总结

因为偶然在牛客网上发现了字节的内推,并且打听到了字节对于未通过候选人不存在“黑名单”处理,只是会存档面试评价,于是就跃跃欲试,想体验一下大厂的面试氛围。因为深知自己还没有做好足够的储备,所以也是抱着查漏补缺的心态的面试,也算满足了年前参加一场大厂面试的2019农历年愿望之一。

在2019农历年年终之际,回顾一下年前对自己的要求:期末全过,圆满完成,而且绩点还是三年中最高,没有辜负考试前的通宵;准备面试,之前每天都有在出博客,算是完成的不错;完成一次大厂面试并记录面试的内容以及遇到的问题及表现,完成。距离2019年的新年还有七天,这篇博客也应该是2019年农历年的最后一篇了,我要开始享受年前七天 Vocation 了。

还是那句话,脚踏实地,无所畏惧。对于我来说,年轻的意义就在于迎接挑战,然后在每一次挑战中享受竭尽全力的过程。谋事在人,成事在天,与其一帆风顺,不如背水一战。对于现在的我来说,置之死地而后生的快感远胜于按部就班完成已知工作的踏实。

附录

HTTP 头属性表格 / 表格来自爱博子

Header 解释 示例
Accept-Ranges 表明服务器是否支持指定范围请求及哪种类型的分段请求 Accept-Ranges: bytes
Age 从原始服务器到代理缓存形成的估算时间(以秒计,非负) Age: 12
Allow 对某网络资源的有效的请求行为,不允许则返回405 Allow: GET, HEAD
Cache-Control 告诉所有的缓存机制是否可以缓存及哪种类型 Cache-Control: no-cache
Content-Encoding web服务器支持的返回内容压缩编码类型。 Content-Encoding: gzip
Content-Language 响应体的语言 Content-Language: en,zh
Content-Length 响应体的长度 Content-Length: 348
Content-Location 请求资源可替代的备用的另一地址 Content-Location: /index.htm
Content-MD5 返回资源的MD5校验值 Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==
Content-Range 在整个返回体中本部分的字节位置 Content-Range: bytes 21010-47021/47022
Content-Type 返回内容的MIME类型 Content-Type: text/html; charset=utf-8
Date 原始服务器消息发出的时间 Date: Tue, 01 Nov 2020 08:12:31 GMT
ETag 请求变量的实体标签的当前值 ETag: “737060cd8c284d8af7ad3082f209582d”
Expires 响应过期的日期和时间 Expires: Thu, 01 Dec 2010 16:00:00 GMT
Last-Modified 请求资源的最后修改时间 Last-Modified: Tue, 01 Nov 2020 12:00:00 GMT
Location 用来重定向接收方到非请求URL的位置来完成请求或标识新的资源 Location: http://www.xxx.com/xxx.html
Pragma 包括实现特定的指令,它可应用到响应链上的任何接收方 Pragma: no-cache
Proxy-Authenticate 它指出认证方案和可应用到代理的该URL上的参数 Proxy-Authenticate: Basic
refresh 应用于重定向或一个新的资源被创造,在5秒之后重定向(由网景提出,被大部分浏览器支持) Refresh: 5; url=http://www.xxx.com/xxx.html
Retry-After 如果实体暂时不可取,通知客户端在指定时间之后再次尝试 Retry-After: 120
Server web服务器软件名称 Server: Apache/1.3.27 (Unix) (Red-Hat/Linux)
Set-Cookie 设置Http Cookie Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
Trailer 指出头域在分块传输编码的尾部存在 Trailer: Max-Forwards
Transfer-Encoding 文件传输编码 Transfer-Encoding:chunked
Vary 告诉下游代理是使用缓存响应还是从原始服务器请求 Vary: *
Via 告知代理客户端响应是通过哪里发送的 Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
Warning 警告实体可能存在的问题 Warning: 199 Miscellaneous warning
WWW-Authenticate 表明客户端请求实体应该使用的授权方案 WWW-Authenticate: Basic