大家好,我是白露。 一个网友跟我分享说,来腾讯三周,已经对大厂祛魅了。 6.26 来腾讯三周,已经对大厂祛魅了 - 图1 三周时间做的事情说多不多说少不少,由于mentor的职级已经是t10了,目前更多的是运维部署工作 这位同学就跟着师兄完成了一些部署任务,修改了6个小缺陷,还做了一个小需求(数据迁移,主要是新建一个表和编写一些基础的增删改查操作)。 通过完成这些任务,他很快就了解到代码架构的样子。 然后发现代码规范没有我想象中的那么严格。hh 师兄说:“实现功能就行,怎么写都行。” 但是这好像,跟他上家公司实习的体验大不一样啊。虽说规模没腾讯大,但在很多细节上很严格。比如,新建一个数据表需要走审批和工单流程,获得批准后才可以去建表。 而在腾讯,师兄说只要建表时给他看一眼语句就行。另外,service调用store时也没有明确的分类限制,只要能实现功能就好。 不得不说,环境熟悉起来真的头大(因为大厂的东西太多了~)。大厂里的各种网站和术语多得让人晕头转向。 比如,代码合并不叫合并,而是叫“合流”,刚开始听到这些专有名词,真是很懵。 福利方面,各部门差别很大 有些部门很养老,6点下班后还能吃个饭打个球;但有的部门10点下班甚至更晚。 他们部门似乎特别忙,健康日也不早下班,只有他6点吃了饭后下班去吃甜点。

团建活动更是如此,有的部门一个月两次小团建,而网友所在的部门已经两年没团建了。。。

一句话来说,腾讯的实习还算蛮舒服的 师兄给的压力很小,他是独立工作的人,任务需要主动去要,或者让隔壁师兄分配。 隔壁师兄前年暑期转正,跟他交流起来效率很高。 不过,有一点让这位同学觉得不太满意,就是腾讯没有配发Mac笔记本,只有Windows台式机,感觉特别不方便。 哈哈,看了这位同学的经历,我想起了之前实验室同门师兄给我分享的,同一个组,负责不同的项目差别也很大,有的老项目还是用python写的 实验室的师兄之前是写C++的,他们组在把Python 改go 中,直接新学两种语法。。。那屎山代码头都大了。 另外,之前鱼皮在论坛里也分享过,同在大厂,不同小组的体验都是天差地别的,薪资年终、业务、日常工作、管理模式、卷的程度等等。 如果有小伙伴还在实习的话,觉得当前实习部门不好后面还可以争取换个部门。 今天我们就来看一次i腾讯的场景题面试,关注我,发送秋招提前批,就可以获取提前批资料了哦~

场景题目-Map

面试官: 同学你好,我们来讨论一下LRU缓存如果LRU缓存不用map指向迭代器会怎么样

求职者: 如果LRU缓存不用map指向迭代器,会导致以下问题:

  • 查找效率低:每次需要查找一个元素时,必须从头遍历链表或数组,时间复杂度为O(n),而使用map查找时间复杂度为O(1)
  • 更新效率低:每次访问一个元素后,需要将其移动到表头或数组头部,这也需要遍历找到该元素,时间复杂度为O(n)
这样会显著降低LRU缓存的性能,特别是在高并发和高访问频率的场景下。

面试官: 那么,我们来分析一下数组实现和链表实现的缺陷。你能先说说数组实现的缺陷吗

求职者: 当然。使用数组实现LRU缓存的主要缺陷有:

  • 固定大小:数组的大小是固定的,不容易扩展或缩减。
  • 插入和删除效率低:在数组头部插入或删除元素需要移动大量元素,时间复杂度为O(n)
  • 空间利用率低:预分配的数组空间可能会浪费,如果实际数据量远小于数组大小,会浪费内存资源。
这些缺陷使得数组在实现动态和频繁更新的LRU缓存时表现不佳。

面试官: 那么,链表实现又有哪些缺陷呢?

求职者: 使用链表实现LRU缓存的主要缺陷有:

  • 空间开销大:每个节点需要额外存储前驱和后继指针,占用更多内存。
  • 局部性差:链表节点在内存中可能不连续,访问时容易产生缓存未命中,降低访问速度。
  • 实现复杂:双向链表的插入和删除操作需要处理前驱和后继指针,容易出错,代码复杂度较高。
尽管链表在插入和删除操作上比数组更高效,但其空间开销和实现复杂度依然是不可忽视的问题。

面试官: 好的,我们再深入一点。假如我们要实现一个高效的LRU缓存,你会怎么做?能提供一个C++的实现吗?

求职者: 当然可以。高效的LRU缓存可以使用哈希表双向链表结合的方式来实现。哈希表用于快速查找,双向链表用于维护访问顺序。

以下是一个C++的实现示例:
  1. #include <unordered_map>
  2. #include <list>
  3. class LRUCache {
  4. private:
  5. int capacity;
  6. std::list<std::pair<int, int>> cacheList;
  7. std::unordered_map<int, std::list<std::pair<int, int>>::iterator> cacheMap;
  8. public:
  9. LRUCache(int capacity) : capacity(capacity) {}
  10. int get(int key) {
  11. if (cacheMap.find(key) == cacheMap.end()) {
  12. return -1;
  13. }
  14. cacheList.splice(cacheList.begin(), cacheList, cacheMap[key]);
  15. return cacheMap[key]->second;
  16. }
  17. void put(int key, int value) {
  18. if (cacheMap.find(key) != cacheMap.end()) {
  19. cacheList.splice(cacheList.begin(), cacheList, cacheMap[key]);
  20. cacheMap[key]->second = value;
  21. return;
  22. }
  23. if (cacheList.size() == capacity) {
  24. int delKey = cacheList.back().first;
  25. cacheList.pop_back();
  26. cacheMap.erase(delKey);
  27. }
  28. cacheList.emplace_front(key, value);
  29. cacheMap[key] = cacheList.begin();
  30. }
  31. };

面试官: 请解释一下你的代码。

求职者:

  • capacity:缓存的容量。
  • cacheList:双向链表,存储缓存内容,最新访问的在前。
  • cacheMap:哈希表,键为缓存键,值为链表节点的迭代器,用于快速定位缓存项。

get操作

  • 查找:在cacheMap中查找键,若不存在返回-1
  • 移动到头部:若存在,将对应的链表节点移动到链表头部。
  • 返回值:返回缓存值。

put操作

  • 更新:若键已存在,更新值并移动到链表头部。
  • 插入:若键不存在,先检查缓存是否已满,若满则删除链表尾节点和对应的哈希表项,然后在链表头部插入新节点,并更新哈希表。
这样就能高效地实现LRU缓存,保证了O(1)的时间复杂度进行查找和更新操作。

面试官: 很好,今天的面试就到这里了,感谢你的回答。我们会尽快通知你结果。