目录
- 写在前面
- 阿里
- 阿里数据库
- 美团
- 快手
- 百度
- 科大讯飞
- 一面
- 字节
- 一面
- 快手
- 一面
- 二面
- 三面
- 滴滴1-3面部分
- 米哈游
- 一面
- 京东
- 二面
- 美团
- 二面
- 三面
- 腾讯
- 一面
- 二面
- 三面
- 高德
- 一面
- 二面
- 三面
- 大文娱
- 一面
- 蚂蚁
- 二面
写在前面
博主24届985硕士非科班,秋招面了挺多大厂,拿了阿里、百度、美团、快手、京东、oppo、联想、滴滴等中大厂offer,博主属于是C++ Java双修,水平相较于科班属于比较垃的存在。这里分享一下其中一些面经,有些可能记录漏了,大家看看就好。
阿里
3.29晚 简历面
-
ping不通谁返回ICMP报文?最后一个路由器。
-
模板有什么好处?有什么坏处?
优点:
- 灵活性, 可重用性和可扩展性;
- 可以大大减少开发时间,模板可以把用同一个算法去适用于不同类型数据,在编译时确定具体的数据类型;
- 模版模拟多态要比C++类继承实现多态效率要高, 无虚函数, 无继承;
缺点:
- 易读性比较不好,调试比较困难;
- 模板的数据类型只能在编译时才能被确定;
- 所有用基于模板算法的实现必须包含在整个设计的.h头文件中, 当工程比较大的时候, 编译时间较长;
-
STL为什么用模板?
-
还有什么方法实现多态除了虚函数?
- std::function c++17里面的any和variant boost里面的any 类型擦除 模板实现静态多态
-
协程有什么缺点?
- 无法利用多核资源:协程的本质就是单线程,它不能同时将单个CPU的多核用上,协程需要配合进程才能运行在多核CPU上。
- 协程一旦阻塞,线程就阻塞,影响其他协程
-
线程池怎么实现的?
-
深拷贝和浅拷贝?什么情况下深拷贝,什么情况下浅拷贝?
-
虚拟内存?
-
算法
- 最长有效括号。
- 字符串乘法
这个面试官估计写java的,我跟他说移动构造函数他不知道… 问的问题有些百度都搜不到,为什么STL用模板?我就只知道扩展性,实现多态的其他方法,动态多态除了虚表还能有啥,下来一想只有any这种了,他说模板多态,但那是静态多态,编译期就确定了。算法题都做过但是记不起来了,不过这个算法是走流程,我直接手机打开写的。总之,感觉自己leetcode还不熟,难题背不到的就不会…
4.3阿里笔试:还行
4.9 二面
问了些经典八股,浏览器输入url全流程、协程线程进程区别、select poll epoll区别et.al
算法口述:最小栈
反问:问有多少面,他说下一次面就是交叉面了,意思这一面过了?
5.7 三面
深挖MiniKV项目,从技术选型到架构,gRPC,性能等。反问部门业务,貌似是做云服务器的,也就是用户云虚拟机的任务编排啊等等,其他部门例如存储部门给他们提供功能接口。
问最近除了做项目还有学习什么技术么?答 CMU15-445数据库内核。
offer了哈 之前忘更新了 5.7过后隔了几天hr面 然后第二天意向书
阿里数据库
通过校友投的,约面4.6 19:00。
八股就不说了,倒是来了个有意思的问题:c怎么实现多态,他说函数指针巴拉巴拉。还有LSM树,字典树。
场景题:
- 一亿元全国红包,怎么分,高并发怎么解决?
- 全国分省分配红包巴拉巴拉
算法题:
- 1000w个字符串,不重复的估计就300w个,找出频次最高的10个,内存限制1G。
- 默写了个topK问题,哈希表加大顶堆解法。
- 这类面试题有经典面经:https://blog.csdn.net/v_JULY_v/article/details/6279498 总结下来就是大文件通过hash变小文件然后分治,之后归并。
二面 4.12晚7点
- 上来就是问项目 这个人非常技术大佬
- udp可靠协议怎么写
- 1、添加seq/ack机制,确保数据发送到对端。
- 2、添加发送和接收缓冲区,主要是用户超时重传。
- 3、添加超时重传机制。
详细说明:送端发送数据时,生成一个随机seq=x,然后每一片按照数据大小分配seq。数据到达接收端后接收端放入缓存,并发送一个ack=x的包,表示对方已经收到了数据。发送端收到了ack包后,删除缓冲区对应的数据。时间到后,定时任务检查是否需要重传数据。
-
传输层限流怎么做
- 计数1s内请求数,假如只想让1s接受5000条,但是这1s接受了10000条,那下一秒来的消息百分之50给它拒绝掉。
-
muduo为什么快
-
我的minikv性能问题
-
我的minikv加锁粒度问题
- 优化:对每个hash槽单独设置锁
-
算法题
//输入单词提示器 class WordCompleter {//完成单词前,每当输入一个字母,返回已输入字母序列作为前缀的10个词频最高的单词List<String> inputChar(char c);//每当完成一个单词,更新词频void completeWord(String word); }
我拿前缀树做的,但是不是最优解法,他说在每一层维护一个堆是最优的。说实话没想明白。
class Trie
{
public:vector<Trie *> t;int cnt = 0;bool isEnd = false;vector<char> tmp;string path;priority_queue<pair<int, string>> pq;Trie(){t.resize(26);}void insert(string word){Trie *node = this;for (auto &c : word){if (node->t[c - 'a'] == nullptr){node->t[c - 'a'] = new Trie;}node = node->t[c - 'a'];}node->cnt++;node->isEnd = true;tmp.clear();}vector<string> inputChar(char c){tmp.push_back(c);Trie *node = this;for (auto ch : tmp){if (node->t[ch - 'a'] == nullptr){return {};}path += ch;node = node->t[ch - 'a'];}int k = 10;vector<string> res;while (k-- && !pq.empty()){string str = pq.top().second;pq.pop();res.push_back(str);}return res;}void dfs(Trie *node){if (node == nullptr)return;if (node->isEnd == true){pq.push(pair<int, string>(node->cnt, path));return;}for (int i = 0; i < 26; ++i){if (node->t[i] == nullptr)continue;path.push_back('a' + i);dfs(node->t[i]);path.pop_back();}}
};
美团
3.18 上午10笔试 笔试还算顺利 5道题全ac了
3.30 早上11点一面
又是一个java面试官 我服了…不过他人很好 程序员都这么儒雅的么?
不愧是java人,redis一同猛问,还好我写MiniKV的时候了解了比较多。
- redis数据结构 string list
- redis rdb aof 适用场景
- rdb比aof数据恢复速度快?
- redis 过期键删除策略 内存淘汰策略
- tcp udp区别 头部结构
算法:
-
- 将有序数组转换为二叉搜索树 https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/description/ 3分钟a了
4.3约二面时间:4.7 晚7点二面 改时间为4.10 晚7点
疯狂场景题,支付场景。
- gRPC rpc原理
- protobuf json
- 客户端如何感知多个服务端中某个服务端宕机
- 客户提交支付请求,美团后端调银行api超时,怎么办?
- 提醒用户使用其他支付方式
- 这个请求发向银行,但是一直没到,该怎么给用户回,重复扣款问题
- 无算法题…. 问了一堆职业规划等等
我只能说,场景题真不会答,逆天,一点八股不沾。
快手
4.4 晚 7.30一面 推掉了 投成日常实习了…
百度
3.13 笔试。笔试有点难啊,不知道过不过。
3.15 面试通知,约3.19面
3.19一上午三面面完
一面coding:
- 有序数组合并 (没说原地,直接偷鸡O(N)空间复杂度)
- 删除排序链表中的重复元素 II (现场写出了个小bug,但是代码框架都是对的,下来写直接ac…)
二面coding:
- 字符串全排列II (面的时候最后拿set去重,其实应该先排序然后回溯的时候去重)
- 剑指 Offer 41. 数据流中的中位数,给我整了道困难…(要求复杂度nlogn,偷鸡一波写了个n^2)
一些问题:
- 优化虚表查询 https://zhuanlan.zhihu.com/p/563618701
- binlog记录的是什么 statement形式和row形式
- sql题 选一个表中总分最高学生的信息 (stuid classid score)
- json和protobuf优劣,区别,特点
- raft选主失败
- redis list底层(链表或压缩列表)
selecttmp.stuid, tmp.classid, tmp.r
from
(select*, sum(score) as rfrom testgroup by stuidorder byr desclimit 1
) tmp
总结:
一早上三次面试,问的都还算基础,操作系统都没咋问,多态看来是必背科目了… 第二个面试官问raft问的也比较浅,我其实也不太会。一面一道简单一道中等,二面一道中等一道困难,但是这个困难只是最优方法是困难,想ac还是不困难的。
科大讯飞
一面
webserver,client掉线,server怎么处理:read返回0,close掉,进入closed_wait状态。
浏览器输入网址过程,涉及到的协议
epoll处理:请求来甩线程池解析 单reactor多线程
聊了下阿里实习内容
算法口述了下倒数k个链表节点
口述了下快排思想
重载和重写
DHCP
总共也就不到30分钟结束了…
字节
一面
- 进程组?里面的一号进程?
- kill -9的对应信号?
- 优雅关闭怎么做,后台程序怎么退出?MiniKV退出时怎么优雅?
- redis stream结构?
- 如何理解云原生
- docker原理,和VM区别?
- 容器的本质是什么?共用什么?
- docker如何进入一个容器?
docker exec -it <容器ID或容器名称> <命令>
- 进程线程区别?写时拷贝等
- mysql慢查询
- vanus和其他中间件的区别
- Linux进程打开了哪些文件描述符、资源怎么看(/proc文件夹)
- Linux如何查看进程资源占用(top)
- Linux查看文件最后十行(tail)
- git rebase 和merge cherry-pick
- 算法:多线程ping ipList,整个ping的过程有timeout,获取ping结果。
- 算法:构造一个二叉树的镜像二叉树,不能对原二叉树修改。
反问
- 做AI服务的,偏工程,golang和python居多
快手
一面
- 合并链表并删除重复元素(o(1)空间复杂度,考虑内存泄漏)
- 自己实现一个Vector
- MiniKV项目持久化的时候遇到删除Key怎么办?
二面
- 两个集合求交集极致优化(二分+索引)
- MiniKV项目
做搜广推计算引擎的。
#include <bits/stdc++.h>
using namespace std;
// version1: O(n + m)
vector<int> solver1(const vector<int>& v1, const vector<int>& v2) {int ptr1 = 0, ptr2 = 0;vector<int> res;while (ptr1 < v1.size() && ptr2 < v2.size()) {if (v1[ptr1] < v2[ptr2]) {ptr1++;} else if (v1[ptr1] == v2[ptr2]) {res.push_back(v1[ptr1]);ptr1++;ptr2++;} else {ptr2++;}}return res;
}
// version 2: 双边二分,O(log(m + n))
vector<int> solver2(vector<int>& v1, vector<int>& v2) {int ptr1 = 0, ptr2 = 0;vector<int> res;auto bSearch = [](vector<int>& v, int left, int right, int target)->int {while (left <= right) {int mid = left + (right - left) / 2;if (v[mid] > target) {right = mid - 1;} else if (v[mid] < target) {left = mid + 1;} else {left = mid + 1;}}return right;};while (ptr1 < v1.size() && ptr2 < v2.size()) {if (v1[ptr1] < v2[ptr2]) {int idx = bSearch(v1, ptr1 + 1,(int) (v1.size() - 1), v2[ptr2]);if (idx == ptr1) ptr1++;else ptr1 = idx;} else if (v1[ptr1] == v2[ptr2]) {res.push_back(v1[ptr1]);ptr1++;ptr2++;} else {int idx = bSearch(v2, ptr2 + 1,(int) (v2.size() - 1), v1[ptr1]);if (ptr2 == idx) ptr2++;else ptr2 = idx;}}return res;}int main() {vector<int> v1 = {3, 7, 8, 11, 35};vector<int> v2 = {8, 9, 10, 35};auto v = solver2(v1, v2);for (auto n : v) {cout << n << endl;} cout << endl;return 0;
}
三面
算法:设计kv缓存,支持set get del sample,sample要求复杂度低。key val都可以当int来设计。
#ifndef KVCACHE_WITH_SAMPLE_H
#define KVCACHE_WITH_SAMPLE_H
#include <unordered_map>
using namespace std;
// KVcache,支持get set del sample,sample返回随机一个val,要求时间复杂度较低,其中key不重复(快手三面题)
template <class T>
class KvCacheWithSample {
private:struct Entry {int key;T val;bool used;};unordered_map<int, Entry> dataMap;unordered_map<int, int> reverseMap;int count;int delTimer;void clean() {unordered_map<int, Entry> newDataMap;count = delTimer = 0;reverseMap.clear();for (auto& [k, v] : dataMap) {if (v.used == false) continue;newDataMap[count++] = v;reverseMap[v.key] = count - 1;}dataMap = std::move(newDataMap);}
public:KvCacheWithSample() : count(0), delTimer(0) {}void set(int key, T val) {if (reverseMap.count(key) != 0) {int idx = reverseMap[key];dataMap[idx].val = val;return;}Entry e{key, val, true};dataMap[count++] = e; reverseMap[key] = count - 1;}T get(int key) {if (reverseMap.count(key) == 0) {return T();}int idx = reverseMap[key];if (dataMap[idx].used == false) {return T();}return dataMap[idx].val;}void del(int key) {if (reverseMap.count(key) == 0) {return;}int idx = reverseMap[key];reverseMap.erase(key);dataMap[idx].used = false;delTimer++;if (delTimer % 10 == 0) {clean();}}T sample() {int randIdx = rand() % count;if (dataMap[randIdx].used == true) {return dataMap[randIdx].val;}int ptr = randIdx + 1;while (ptr != randIdx) {ptr = ptr % count;if (dataMap[ptr].used == false) {ptr++;continue;} else {return dataMap[ptr].val;}}return T();}};#endif
滴滴1-3面部分
- raft脑裂怎么处理,raft逻辑怎么保证一致性
- kafka高并发,为什么
- mysql主从不一致可能什么导致的
- mysql吞吐相较于kafka差很多,为什么
- minikv内存清理怎么做的
- 算法三数之和、字符串处理、打印数组中超过半数元素
- mysql事务 原子性实现 mvcc
- nginx进程之间对等么 底层怎么实现
米哈游
一面
- 红黑树的高度最坏情况下为2log(N+1)
- 进程cpu 100%,怎么排查问题
- 信号捕获等
- nagle算法?nagle算法会带来什么问题?怎么关闭nagle算法?
- 可以在 Socket 设置
TCP_NODELAY
选项来关闭这个算法(关闭 Nagle 算法没有全局参数,需要根据每个应用自己的特点来关闭)。
- 可以在 Socket 设置
算法:
- atoi
- 寻找第k大元素
算法都a了。
反问:预研游戏C++服务器开发
京东
二面
close_wait较多什么原因?
epoll底层红黑树每个进程一个还是共享?
A:在多进程的环境中,红黑树是共享的,而不是每个进程都有自己的独立红黑树。当你在多个进程中使用epoll
系统调用时,每个进程都可以通过相同的epoll
实例来访问和管理相同的红黑树。
这是因为在底层实现中,epoll
使用标准的文件描述符(通常是/dev/epoll
)来与内核进行通信。所有的epoll
实例都共享相同的内核数据结构,其中包括红黑树。每个进程可以通过相同的文件描述符来与内核进行交互,对红黑树进行读取、修改和操作。
这种共享的设计使得多个进程可以同时监视同一个epoll
实例所管理的文件描述符集合,并根据事件进行相应的处理操作。这对于实现高效的事件驱动型应用程序非常有用。
需要注意的是,当一个进程从epoll
中删除文件描述符时,此操作仅影响到调用进程自身对epoll
的接口。其他进程对相同的epoll
实例和红黑树不受影响。每个进程可以独立控制自己所监视的文件描述符集合,而不会对其他进程产生影响。
总结起来,epoll
底层红黑树在多进程环境中是共享的,多个进程可以使用相同的epoll
实例与内核共享和操作红黑树数据结构。
C调用C++函数怎么搞?
A:C++函数库在封装的时候接口extern “C”
例如:
// C++ code:
extern "C" void f(int);
void f(int i)
{// ...
}
动态多态和模板多态谁效率高?(模板)
美团
二面
- coredump(segment fault)如何排查?
Segmentation fault (core dumped)错误常见原因总结_改个名字真不容易�的博客-CSDN博客
三面
- cpu为何擅长做逻辑运算
- 算法:最长递增子序列并打印子序列(再创一个记录上一个节点的数组就能打印了)
腾讯
一面
- https双向认证
- CA
算法:
- shared_ptr
- LRUCache
- 斗地主欢乐豆快速查找排名
二面
谈人生
三面
聊云盒
高德
一面
- 一个文件写入量很大,查看谁写的?
lsof | grep job_auto_find.log
- 如何写出命中cache更高的程序?
- 计算型密集任务,将线程绑定到某个核心,避免切换核心导致L1 L2Cache丢失。
- 遍历时候尽量顺序读,e.g. 遍历arr[n][n],一行一行遍历比一列一列遍历快。
- 分支预测
- 服务器请求次数过多大于线程接受能力怎么办?
- core dump排查
- gdb core文件看栈帧
- 内存泄漏排查?
- mtrace memcheck
- 某进程压测怎么都打不到cpu高负载,什么问题引起的?
- 可能是IO型任务
- 线程池处理任务,线程池最大线程数量小,配合拒绝策略拒绝了大部分请求
- 协程可以跨线程调度么?
- 可以
- 协程分类和调度策略
- 对称非对称,内核和用户级别;抢占式、协作、混合调度
- gdb命令?
- rpc性能怎么考量?其他rpc框架了解么?
- grpc thrift dubbo zeromq…
- new一万个对象会怎么样?
- 查看内存使用量,除了top还有什么?
- ps -aux | grep [进程名]
- free -h
- 共享内存是属于进程的还是属于谁的?进程挂了共享内存给谁?
- 共享内存是系统级资源,不会受到单个进程挂掉的影响。操作系统会负责回收挂掉进程所持有的共享内存资源,并将该内存区域标记为可供其他进程使用。
- 共享内存分类?
- System V 共享内存 POSIX 共享内存。shmget这种调用属于System V共享内存。
- g++ -g生成RELEASE的代码有什么结果?
- 代码release重构过,可能调试的时候不一致。
- 其他内存池工具?
- tcmalloc
- 算法:树的最小深度dfs和bfs版本(优化)
二面
聊天
三面
- 为什么更多时候用快排而不是归并排序(额外的空间复杂度)
- 陷入内核为什么慢(系统调用)
- 共享内存为什么好
大文娱
一面
- static关键字有什么不好的地方
- CAP为什么只能保证满足两个?
- 算法:多线程打印
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>/*用多线程编程,实现以下输出:
线程1:1
线程1:2
线程1:3
线程2:4
线程2:5
线程2:6
线程3:7
线程3:8
线程3:9
线程1:10
线程1:11
线程1:12
直到100
*/std::mutex mtx;
std::condition_variable cv;
int currentNum = 1;
int maxNum = 100;
int curThreadId = 1;void printNumber(int num, int threadId) {std::unique_lock<std::mutex> lock(mtx);while (currentNum <= maxNum) {if (curThreadId == threadId) { // 每个线程负责自己的序列std::cout << "线程" << threadId << ": " << currentNum << std::endl;++currentNum;if ((currentNum - 1) % 3 == 0 && currentNum > 1) {curThreadId++;if (curThreadId == 4) {curThreadId = 1;}}cv.notify_all(); // 唤醒其他线程} else {cv.wait(lock); // 等待其他线程输出}}
}int main() {std::thread t1(printNumber, 1, 1);std::thread t2(printNumber, 2, 2);std::thread t3(printNumber, 3, 3);t1.join();t2.join();t3.join();return 0;
}
蚂蚁
二面
- 算法:先反转链表,再2个一组反转链表