分享一波题主字节跳动AML组的面经,可能是凉经,问的不难,但是感觉没有回答好。
---
更新一波面试状态,一面已经通过。二面冲冲冲。
---
- 自我介绍
- 项目1 - 极简版抖音后端,字节跳动青训营项目
- 青训营参加人数,有获得什么奖吗? (内心OS:...)
- 介绍一下优化 Redis 淘汰策略为热门视频设置递增 TTL
- Redis二级缓存用了哪些数据结构,是HashMap吗?
- 在Redis缓存中如何对热门视频和非热门视频进行转换?有用到分布式计数吗?转换时怎么操作HashMap数据结构的?
- Redis持久化策略?(RDB和AOF结合)
- Kafka实现异步写入时如何保证生产者和消费者两端的数据一致性?有用到事务吗?
- 项目2 - Apache ShardingSphere,Google编程之夏
- 介绍一下配置的热更新
- 贡献了3000+行代码,核心代码和测试代码分别有多少?
- 八股:
- Golang语言中array和slice底层数据结构分别是什么?二者有什么区别?
- Vim中如何删除一行数据?
- Docker和虚拟机比有什么缺点?
- Git中的rebase和squash你有用到吗?能讲讲你具体的应用场景的吗?
- Git中的cherry-pick使用什么场景使用?
- 算法题:
- 实现二叉树的中序遍历
// 递归实现
void inorderTraversal(TreeNode* root) {
if (root == nullptr) {
return;
}
inorderTraversal(root->left); // 遍历左子树
cout << root->val << " "; // 输出根节点
inorderTraversal(root->right); // 遍历右子树
}
// 栈实现
void inorderTraversal(TreeNode* root) {
stack<TreeNode*> s;
while (root != nullptr || !s.empty()) {
if (root != nullptr) {
s.push(root); // 将当前节点入栈
root = root->left; // 继续遍历左子树
} else {
root = s.top();
s.pop();
cout << root->val << " "; // 输出根节点
root = root->right; // 遍历右子树
}
}
}
- LRUCache
// 重点是弄清楚cache里的key和value分别是什么
struct DLinkedNode {
int key, value;
DLinkedNode* prev;
DLinkedNode* next;
DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}
DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};
class LRUCache {
private:
unordered_map<int, DLinkedNode*> cache;
DLinkedNode* head;
DLinkedNode* tail;
int size;
int capacity;
public:
LRUCache(int _capacity): capacity(_capacity), size(0) {
// 使用伪头部和伪尾部节点
head = new DLinkedNode();
tail = new DLinkedNode();
head->next = tail;
tail->prev = head;
}
int get(int key) {
if (!cache.count(key)) {
return -1;
}
// 如果 key 存在,先通过哈希表定位,再移到头部
DLinkedNode* node = cache[key];
moveToHead(node);
return node->value;
}
void put(int key, int value) {
if (!cache.count(key)) {
// 如果 key 不存在,创建一个新的节点
DLinkedNode* node = new DLinkedNode(key, value);
// 添加进哈希表
cache[key] = node;
// 添加至双向链表的头部
addToHead(node);
++size;
if (size > capacity) {
// 如果超出容量,删除双向链表的尾部节点
DLinkedNode* removed = removeTail();
// 删除哈希表中对应的项
cache.erase(removed->key);
// 防止内存泄漏
delete removed;
--size;
}
}
else {
// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
DLinkedNode* node = cache[key];
node->value = value;
moveToHead(node);
}
}
void addToHead(DLinkedNode* node) {
node->prev = head;
node->next = head->next;
head->next->prev = node;
head->next = node;
}
void removeNode(DLinkedNode* node) {
node->prev->next = node->next;
node->next->prev = node->prev;
}
void moveToHead(DLinkedNode* node) {
removeNode(node);
addToHead(node);
}
DLinkedNode* removeTail() {
DLinkedNode* node = tail->prev;
removeNode(node);
return node;
}
};