从 DFS 遍历到抖音推荐算法:前端工程师的硬核复习笔记

前言

最近在做算法题和大厂业务梳理时,我发现很多看似独立的技术点,底层逻辑其实是高度相通的。从二叉树的深度优先搜索(DFS),到 JS 数组的栈操作,再到抖音背后的多模态推荐算法,它们都在解决同一个问题:如何在海量数据中,高效地寻找、遍历并提取有价值的信息。

本文将结合我近期的代码实践与学习笔记,从算法基础、数据结构到 AI 前沿,做一次深度的复盘与串联。

一、 算法基石:DFS 遍历与 JS 数组的"潜规则"

在处理树形结构时,深度优先搜索(DFS)是我们的老朋友。它的核心思想是"一条路走到黑,再回溯"。但在实际写代码时,几个细节往往决定了程序的成败。

1. 递归版 DFS 的"收集器"设计

在遍历树时,我们通常需要一个容器来收集节点值。最优雅的写法是引入一个外部传入的数组参数:

javascript

编辑

scss 复制代码
1function dfs(root, res) {
2    if (!res) res = []; // 最佳实践:避免使用默认参数 res=[] 导致的引用污染
3    if (!root) return res;
4    
5    res.push(root.val); // 前序遍历:先处理根节点
6    dfs(root.left, res);
7    dfs(root.right, res);
8    return res; // 注意:返回的是数组,而不是 res.length
9}

复习要点

  • 为什么用 res 参数? 充当"全局收集器",避免在递归返回时频繁使用 [...leftRes, ...rightRes] 展开数组,将时间复杂度从 O(N²) 优化到 O(N)。
  • push 的方向 :JS 的 push 是在队尾添加元素,这完美契合了前序遍历"先访问的节点排在前面"的顺序。

2. 迭代版 DFS:栈(Stack)的逆序艺术

递归虽然优雅,但在树深度过大时容易栈溢出。使用手动栈(Stack)实现迭代是必考题:

javascript

编辑

scss 复制代码
1function dfsPreOrder(root){
2    if(!root) return [];
3    const stack = [root];
4    const res = [];
5    
6    while(stack.length){
7        const node = stack.pop(); // 后进先出 (LIFO)
8        res.push(node.val);
9        
10        // 核心灵魂:先压右节点,再压左节点
11        if(node.right) stack.push(node.right);
12        if(node.left) stack.push(node.left);
13    }
14    return res;
15}

复习要点 :为什么要先右后左入栈?因为栈是"后进先出",为了保证弹出的顺序是"先左后右",入栈时必须反着来。如果改成先左后右,就会变成"从右向左的前序遍历"。

3. 延伸:广度优先搜索(BFS)

与 DFS 的"不撞南墙不回头"不同,BFS 是层序遍历,通常使用队列(Queue)来实现。它的核心思想是"层层递进",先访问离根节点近的节点,再访问远的节点。在实际面试中,经常会要求手写 BFS 来解决"二叉树的最小深度"或"层序遍历"问题。


二、 业务拓展:抖音推荐算法的"漏斗"逻辑

搞懂了 DFS 的遍历逻辑,我们再来看抖音的推荐算法。其实,推荐系统本质上也是一棵巨大的决策树,算法需要在这棵树上进行高效的遍历和打分。

1. 第一步:多模态架构(看懂视频)

在 AI 眼里,视频不是像素的堆砌,而是高维的语义向量(Embedding)。抖音通过多模态技术,将视频转化为 512 维度的向量坐标:

  • 视觉特征:使用 Vision Transformer 逐帧提取画面,不仅能识别"猫"或"程序员",还能通过光影色彩判断情绪基调(温暖治愈或冷酷硬核)。
  • 音频与文本:通过 ASR 技术将配音转为文本,再结合 NLP(自然语言处理)进行语义分析,提取背景音乐的节奏和说话人的情绪。

最终,一个视频在算法眼里变成了坐标系里的一个点(例如:科技维度 0.85,幽默维度 0.05)。

2. 第二步:推荐漏斗(千人千面)

面对千万级的视频库,算法如何在一秒内挑出你最爱看的那一条?这依赖于经典的四层漏斗:

  1. 召回(双塔模型与协同过滤) :广撒网。一侧是用户兴趣塔,一侧是视频特征塔。通过毫秒级的向量点积(Cosine 相似度)运算,快速筛选出最相关的 1000 条视频。同时,经典的"协同过滤"算法也会基于"物以类聚,人以群分"的逻辑,找到和你兴趣相似的用户,把他们看过的视频推荐给你。
  2. 粗排:用轻量级模型计算作者历史表现、大类匹配度,将 1000 条缩减为 300 条。
  3. 精排(核心主战场) :预测你的行为概率。综合打分公式类似:Score = w1*点击 + w2*完播 + w3*点赞 + w4*评论 + w5*分享。这里通常会使用 Wide & Deep 模型,Wide 部分负责记忆历史高频特征,Deep 部分负责泛化潜在兴趣。
  4. 重排(Re-ranking) :兼顾生态与体验。如果精排前 10 名全是同类视频,重排机制会强制打散(例如每两个硬核内容插入一个跳舞视频),防止用户审美疲劳。

3. 探索与利用(Explore & Exploit)

算法不仅懂你,还有"好奇心"。通常采用 80% 推荐已知爱好(Exploit)+ 20% 探索潜在兴趣(Explore)的策略。如果你在野外求生视频多停留了 3 秒,一个全新的兴趣 Tag 就会被悄悄记录。


三、 概念辨析:那些容易混淆的 Tag

在复习过程中,有几个高频词汇需要明确其边界:

  • Tag(标签) :在推荐系统中,它是用户画像的载体(如 #NBA、#唱跳);在社交媒体上,它是方便搜索的话题标记。
  • 多模态(Multimodality) :打破感官壁垒,让 AI 同时处理文字、图像、音频。它是抖音能精准理解视频内容的前提。
  • ASR + NLP:这是多模态中处理音频的核心链路。ASR 负责"听音成字",NLP 负责"字中取意"(词义消歧、情感分析等)。

四、 面试高频 Q&A 总结

为了增强这篇笔记的实战意义,我整理了几个面试中极易被问到的关联问题:

  1. Q:为什么 JS 中不推荐用 res=[] 作为默认参数?
    A:因为 JS 函数的默认参数只在函数定义时创建一次,多次调用如果不传参,会共享同一个数组引用,导致结果互相污染。应在函数内部判断 if (!res) res = []
  2. Q:DFS 和 BFS 在实际业务中如何选择?
    A:DFS 适合寻找所有可能的解(如解数独、走迷宫),或者需要回溯的场景;BFS 适合寻找最短路径(如社交网络中的好友推荐、地图导航)。
  3. Q:推荐算法中的"信息茧房"是如何被打破的?
    A:主要通过重排阶段的"随机扰动"和"探索机制"。算法会强制注入一定比例(如 10%-15%)的探索型内容,并通过用户的"不感兴趣"等负反馈实时调整画像。

总结

无论是用 Stack 模拟 DFS 遍历二叉树,还是用双塔模型在千万级视频中召回目标, "降维、映射、高效筛选" 是贯穿始终的哲学。希望这篇融合了代码细节与业务宏观视角的笔记,能为你的前端进阶之路提供一点启发。

如果你觉得这篇复习笔记对你有帮助,欢迎点赞收藏,我们下期继续硬核复盘!

相关推荐
zach1 小时前
网页中的虚拟滚动技术是不是源自IOS中的tableview的机制
前端
林希_Rachel_傻希希1 小时前
1小时速通React之Hooks
前端·javascript·面试
柯克七七1 小时前
公司前端项目打包体积从 2MB 降到 400KB,我改了这四个配置
前端
英勇无比的消炎药1 小时前
我才发现这些架构的“拆”与“合”哲学
前端
shen_1 小时前
AI Coding:前端UI规范笔记
前端
llz_1122 小时前
web-第五次课后作业
前端·后端·http
恋猫de小郭3 小时前
Redis 作者反驳「中国模型之所以强,是因为通过 API 蒸馏了美国模型」
前端·人工智能·ai编程
Darling噜啦啦3 小时前
Canvas 游戏开发与数据可视化实战:从飞机大战到 ECharts 报表
前端·echarts·canvas
OpenTiny社区3 小时前
这次更新太良心!GenUI SDK v1.2.0 轻量化 + 稳流式 + 超强 Playground
前端·vue.js·ai编程