☰
第45天实习打卡:堆一题与八股背诵背后的知识串联
2026/10/13 4:01:49 网站建设 项目流程

0x3f,第45天打卡。先解释一下这个ID:刷算法题的人应该都熟,0x3f3f3f3f常被拿来当int的上界,我拿它做博客代号,多少带点竞赛执念。今天原本只是照常推进实习准备:八股背诵第二天,顺手做了一道堆的算法题。按计划,白天都在跟进程调度、优先队列、B+树这些硬骨头较劲,晚上整理笔记时突然愣住——好几条本来没什么关系的知识线,毫无征兆地连到了一起。那种感觉很难描述,如果借用航天里的词汇,大概就是“总观效应”:前一秒还在低头处理单个知识点,后一秒突然看见整张知识地图的全貌。这篇就把第二天背诵的内容、那道堆题、还有这次“不得了”的认知串联一起复盘。

1. 面向实习的八股背诵:第二天,我背出方法论了

1.1 八股不是洪水猛兽,它是知识骨架

我在准备实习之前,和不少人一样,看到“八股”两个字就皱眉。总感觉这是背答案、走捷径、没技术含量。可等我真正开始复习才发现,之所以觉得它没技术含量,是因为过去只会用碎片化方式去记,而不是把它当成知识骨架来搭。

实习生面试要考察的东西其实很朴素:语言基础、数据结构、操作系统、网络、数据库。这些科目的标准问题就是一张知识地图的主干。比如问“进程和线程的区别”,如果你只能背出“进程是资源分配单位,线程是调度单位”,那确实是八股。但如果你能接着讲出:为什么要有虚拟内存、进程切换为什么重、线程为什么共享地址空间、什么时候要开多进程什么时候要多线程、协程又优化在哪——那就不是背诵,而是把整个操作系统里的内存管理和并发设计串起来了。面试官问八股,本质是想看候选人对基础概念的“结构化理解”。

所以第二天我调整了心态:八股不是用来“背”的,是用来“搭骨架”的。一题一背只是血肉,概念之间的因果连接才是骨头。

1.2 第二天的背诵计划:三张纸搞定一天

我给自己定的背诵节奏是:每天一个大方向,拆成三个时间段,上午背、下午刷题、晚上输出整理。

第二天的主线是操作系统和它周边。具体安排:

  • 上午:进程管理模块,重点搞定进程与线程、状态切换、调度算法。
  • 下午:算法题巩固,用一道堆题保持手感和理论结合。
  • 晚上:网络模块继续推进,加上当天的整体复盘。

每背完一个主题,我会在A4纸上写三个关键词:问题、解法、迁移点。比如进程调度,关键词就是“怎么选下一个执行”、“时间片+优先级+抢占”、“和堆/优先队列关系密切”。后面那句就是今天产生总观效应的线索。不要小看这种三词笔记,它逼着你把一段长答案压缩成可以迁移的抽象结构,下次遇到类似问题就能快速调用。

1.3 怎么确保不是白背:录音口述与关键词卡片

第一天我踩过坑:看了以为会,合上笔记脑子空白。所以第二天改成“合书口述校准法”。具体做法:把问题写在该页的角落,手机录音,自己对着空气当面试官讲一遍,讲完去听录音,和标准答案比对,重点不是原话像不像,而是有没有把因果讲清楚。这个过程很痛苦,但特别有效。

另一招是关键词卡片:一张卡片只写五六个关键词,关掉手机和资料,看着关键词说三分钟。能说出来,才说明这条八股从“认识”变成了“掌握”。我第二天晚上就是这么复习进程通信的,嘴上说着“管道、消息队列、共享内存、信号量”,突然发现信号量的使用场景说得不够精确,马上标记成第三天的补漏项。你看,背诵不是终点,它只是帮我们照出知识漏洞的镜子。

2. 堆一题:数据流中位数,和它背后的堆内核

2.1 为什么我选了这一道堆题

标题里写“堆一题”,今天具体做的是数据流中位数。这题是优先队列的经典应用,正好能和我上午背的进程调度优先级队列呼应上。刷题时我习惯先不看答案,先想最笨的办法:来一个数插进数组排一次序,取中间值。复杂度O(n^2),显然浪费。核心痛点是:数据在不停到达,就像排队买奶茶的人源源不断,你不可能每次来一个人就让大家重新排队,最好有一个动态结构能随时知道队伍中间是谁。

堆就是答案。最大堆管小的那一半,最小堆管大的那一半,两个堆各提供一个堆顶,中位数就由它俩算出来。

2.2 堆的底层原理:完全二叉树加上浮下沉

要会写堆,先得会用手写裸堆才算踏实。C++里priority_queue是封装好的,但面试有时候会让你模拟过程,甚至手写堆排序。堆的物理结构是数组,逻辑结构是完全二叉树。父节点下标是i,左孩子2i+1,右孩子2i+2(从0开始)。堆的性质很简单:大顶堆里父节点大于等于孩子,小顶堆反过来。

插入元素时放到尾部,然后不断上浮;删除堆顶时把最后一个元素移到堆顶,再不断下沉。上浮和下沉每次都只和一条路径上的节点比较,所以复杂度是O(logn)。这个结构用来解决动态极值问题非常合适:它不保证全局有序,只保证极值在树根,很多场景要的恰恰只是极值。生活里也是一样,你不需要把整个队伍按身高排好,只需要知道最高的是谁,那堆就是为这种需求量身定做的。

2.3 双堆解法代码与易错点

数据流中位数用一个大根堆maxHeap存较小的一半,小根堆minHeap存较大的一半。每次插入先决定进哪个堆,然后强制两个堆的容量差不超过1。代码在这里:

class MedianFinder { public: MedianFinder() {} void addNum(int num) { // 默认先进左半部分的大顶堆 if (maxHeap.empty() || num <= maxHeap.top()) { maxHeap.push(num); } else { minHeap.push(num); } // 再平衡:让大顶堆要么和小组一样大,要么多1个 if (maxHeap.size() > minHeap.size() + 1) { minHeap.push(maxHeap.top()); maxHeap.pop(); } else if (minHeap.size() > maxHeap.size()) { maxHeap.push(minHeap.top()); minHeap.pop(); } } double findMedian() { if (maxHeap.size() == minHeap.size()) { return (maxHeap.top() + minHeap.top()) / 2.0; } return maxHeap.top(); } private: priority_queue<int> maxHeap; // 大顶堆 priority_queue<int, vector<int>, greater<int>> minHeap; // 小顶堆 };

有个易错点:C++里priority_queue默认是大顶堆,要建小顶堆必须写完整三个模板参数priority_queue<int, vector<int>, greater<int>>,漏了编译期就报错。另一个容易踩的坑是findMedian里动态取堆顶前要保证两个堆不为空,否则会有未定义行为。空数据流时返回什么,提前和面试官约定好,比如返回0.0。这种边界细节最常被追问。

2.4 一题带出整个堆家族:TopK、合并有序链表、定时器

数据流中位数只是堆应用的冰山一角。给我最大启发的不是这题本身,而是做完后把同族题目列在一起:

  • TopK问题:维护大小为K的小顶堆,遍历一次,堆顶就是第K大,复杂度O(nlogK)。
  • 合并K个有序链表:把所有链表头放进小顶堆,弹出最小值后补下一个节点。
  • 定时器和任务调度:把任务按到期时间放进小顶堆,每次取出最近到期的执行。

这些题目看着不同,骨架都是同一个:动态集合里需要快速拎出最大或最小。这里补一段TopK的极简示例,也经常被拿来当“堆一题”的变体:

vector<int> topK(vector<int>& nums, int k) { priority_queue<int, vector<int>, greater<int>> pq; for (int x : nums) { if (pq.size() < k) { pq.push(x); } else if (x > pq.top()) { pq.pop(); pq.push(x); } } vector<int> res; while (!pq.empty()) { res.push_back(pq.top()); pq.pop(); } return res; }

这段代码的关键是“小顶堆兜底”,堆顶始终是当前K个最大值里最小的那个,新元素比堆顶大才替换。面试时一旦意识到这些题都是同一个骨架,可以把答案组织得很漂亮:“这里我会用优先队列,因为它就是一个支持动态插入和O(1)取极值的数据结构。”这句话,和我上午背进程调度优先级队列的核心完全打通了。

3. 看见不得了的事情:总观效应是怎么发生的

3.1 先承认:所谓“不得了”,不是八卦,是认知跃迁

看到标题的读者可能期待我写点什么行业猛料,但我先说清楚:我记录里说的“不得了的事情”,不是什么内部消息或八卦,而是发生在自己脑子里的变化。毕竟如果把实习准备当成一个游戏,最大的隐藏奖励不是背下所有答案,而是某天突然看见知识之间的隐藏通路。

第45天晚上,我把当天的笔记按三个方向摊开:进程调度、堆的数据结构、数据库索引。一开始它们各自安好。当我在页边写下“优先队列”四个字时,脑子里闪过一个念头:操作系统里的就绪队列,不就是在维护一个按优先级排列的动态集合吗?如果就绪队列是这样,那消息队列、定时器、TopK、甚至网络里的重传队列,底层都是同一套抽象。那一刻不是掌握了某个新知识,而是旧知识突然排好了队。

3.2 三个让我产生总观效应的具体串联

第一个串联:堆和进程调度。面试高频题是调度算法,先来先服务、短作业优先、时间片轮转、优先级调度、多级反馈队列。你发现没有,除了FCFS,几乎每一种调度都要一个“选谁下一个执行”的机制,这就是优先队列。真到实现层面,一个小顶堆按优先级或截止时间排序,就解决了核心逻辑。于是算法题里的堆和操作系统题的调度算法变成同一道题。

第二个串联:B+树、堆和二分查找。数据库索引喜欢说B+树,那也是一棵搜索树,和堆一样都是树状抽象,只是一个做有序遍历、一个做动态取极值。它们共同点是:用树形结构把O(n)的查找或维护降成对数级。一名后台实习生的面试里,数据结构和数据库看似两个科目,根上都是复杂度优化。

第三个串联:网络、消息队列和限流。TCP的重传队列、消息中间件的积压队列、限流算法里的令牌桶,它们又一次拿了“队列加策略”这个模板。不同的只是具体语义和约束。我顿时理解了为什么面试官常说“基础扎实”——不是知道很多名词,而是看得到名词背后的共同骨架。

3.3 怎么主动制造自己的总观效应

总观效应不会自己来,等你背到一定量,它才会被触发。我复盘时发现,这次能串联起来不是偶然,有前置条件:

  1. 足够多的孤立知识点:没有数量就没有可连接的节点,前45天的积累是关键。
  2. 每天强制做迁移提问:每次背完一个八股,问自己“这能联系到我最近刷的哪道题”。
  3. 留出整理复盘的时间:不能只输入,要写下来,哪怕是潦草的一张纸。

这三个条件缺一个都可能让知识一直散落。所以我才会在标题里写“看见了些不得了的事情”——那其实是我前44天坚持刷题、第2天认真背八股的一起回响。给自己打个卡:继续保持这个节奏,下一个总观时刻应该不会太远。

4. 实习求职路线图:45天是怎么走过来的

4.1 第45天完整时间表参考

给准备实习的同学一个参考模板,不一定照抄,关键是体验这种“上午理论、下午实战、晚上输出”的节奏:

时间段任务形式
08:30-10:00操作系统背诵关键词卡片加口述录音
10:20-11:30网络八股积累对照脑图整理
14:00-16:00堆一题:数据流中位数手写代码、复杂度分析
16:20-17:20相关堆扩展题TopK、合并K个链表
20:00-21:30全天复盘与串联写学习日志,标记明天补漏

别小看最后半小时的复盘,没有它,我今天就不会有总观效应。白天背进脑子的知识,如果不经过整理,就像书架上乱放的书;复盘就是把它们归类上架,下次调用才知道去哪层找。

4.2 前44天走过的四个阶段

简单回顾一下前面44天,方便后来者参考:

  • 第1-14天:语言与基础数据结构复习。我用的是经典教材加简单题,把数组、链表、栈、队列、树的基本操作恢复到可以手写的水平。
  • 第15-35天:算法专项,按模板刷题。每天两到三道,重点覆盖二分、双指针、动态规划、二叉树、图、堆和优先队列。
  • 第36-44天:项目复盘。把自己做过的模拟项目、课设、比赛拿出来,写清楚背景、难点、方案、结果,整理成STAR结构。
  • 第45天开始:八股背诵加每日一题并行。这阶段持续到面试前,同时穿插模拟面试。

如果你也差不多是这个节奏,建议把项目复盘放前面,因为后面的八股和算法都需要用项目场景来落地,不然就是空对空。没有项目经验的实习简历会很难看,哪怕项目是模拟的,也要能把技术难点讲清楚。

4.3 为什么“每天一题加固定八股”是最稳的组合

很多人在实习准备时容易走极端:要么狂刷算法题,要么整天背八股。我自己的体感是,两者必须并行,原因很简单:算法题给的是“解决问题的能力”,八股给的是“解释问题的语言能力”。面一次试你会发现,面试官出一道算法题,做完之后一定追问:为什么这题要用堆?用数组实现会怎样?这题和调度算法有什么关系?如果只刷题不背八股,能写但说不清;如果只背八股,题目一变就懵。

每天只做一道堆这种难度的题,成本不高,但能保证手不生。八股背诵的节奏也不宜贪多,每天精练三到四个问题,第二天复习前一天的内容。45天下来就是100多个问题和几十道算法题,足够撑起一场实习面试。把目标切小,才能长期不断档。

5. 实习准备避坑指南:那些没人提醒你的细节

5.1 八股背诵的三个反模式

我用两天的背诵踩出了一些雷,列出来给后来者排雷:

  • 反模式一:背标准答案原文。面试官一听就知道你在背,关键是你要能临场把逻辑讲出来。正确姿势是记“问题场景、核心答案、延伸关键词”。
  • 反模式二:从不回顾。人脑不是硬盘,间隔重复是必须的。当天晚上看一遍、第三天早上再看一遍、周末总复习,这是最低配置。
  • 反模式三:和算法、项目完全割裂。八股不是为了回答而回答,是为了给面试官展示你的工程理解。背数据库索引时去写一道相关题,背缓存时去复习LRU,效果比分开学加倍。

5.2 堆题目现场最容易翻车的四个瞬间

经常做堆题的人应该都有过这种经历:思路对,代码就是跑不通。常见翻车点:

翻车点典型表现解决方案
堆方向写反小顶堆写成了默认大顶堆记住greater<int>才是小顶堆
忘记再平衡双堆size差超过1每次add后都检查并调整
findMedian只取一个堆顶偶数长度时算错size相等时两个堆顶取平均
堆空访问top未处理空数据流入口处判空返回约定值

这些细节就是面试官喜欢追问的地方,提前自检,等于白送分。我在刷数据流中位数时,第一次就没有判空,直接崩了,和上面表格第一条和第四条各踩了一次。

5.3 第45天的心理建设:怎样稳住长期节奏

坚持到第45天,最大的敌人不是难度,而是无聊。我的方法是每天记录“今天解决了什么问题”,不记录“今天学了多长时间”。解决一个具体问题带来的满足感,比呆坐两个小时高得多。

另一个心法是:允许自己某一天只学一小时,但绝不允许完全断掉。打卡日历上不能断,断了会连锁崩盘。如果背到烦躁,不要硬扛,换个输入方式:看一篇文章、整理一遍旧笔记、甚至对人讲一遍今天学的东西都算复习。重要的是保持接触,而不是每天都要感动自己。实习是三个月甚至半年的持久战,节奏感比爆发力重要。

最后顺手分享一个我今天刚用上的习惯:晚上复盘时,不要只在脑子里说“我懂了”,找一张白纸,把今天背的八股和做的题用三分钟默写出来。写不出来的地方,就是明天该补的位置。这比任何打卡软件都诚实。

第45天,堆一题,背两章,外加一次总观效应,我觉得今天可以收工了。如果你也在准备实习,希望这篇能给你一点实感:知识不是一座座孤岛,你坚持挖的每一条隧道,最终都会在某天打通相邻的两座岛。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询