一、企业概况
1.1公司简介
英伟达(NVIDIA)成立于1993年,总部位于美国加州圣克拉拉,是全球领先的加速计算公司。公司以图形处理器(GPU)起家,业务从游戏显卡扩展到数据中心加速计算、人工智能训练与推理、专业可视化、汽车电子等领域,并通过 CUDA 软件栈构建了从芯片到开发工具再到行业方案的完整生态。英伟达半导体科技(上海)有限公司是其在中国的经营实体之一,承担研发与技术支持相关职能。软件工程师岗位在这里既包括底层驱动、编译器与运行时,也包括上层工具链、平台软件与算法实现,因此面试对代码质量、复杂度分析和系统思维的要求都比较高。
| 成立时间 | 1993年 |
| 总部 | 美国加州圣克拉拉,中国区在上海等地设有研发与业务机构 |
| 主营业务 | GPU 与加速计算芯片、数据中心与人工智能解决方案、专业可视化、汽车电子 |
| 技术特征 | 以 GPU 硬件加 CUDA 软件栈构成生态,软硬件协同是核心竞争力 |
1.2岗位定位
软件工程师在英伟达的分工很细,可能落在驱动与固件、编译器与运行时、深度学习框架适配、平台工具或应用算法几条线。校招进来一般先做模块级的开发与调试,逐步承担子系统设计。这个岗位的特点是软硬件交界多:写的是软件,但要理解内存层次、并行度和硬件行为。所以面试不会只考算法题的对错,还会看你对复杂度、边界和工程权衡的敏感度。
| 所属体系 | 软件研发体系,按驱动、编译器与运行时、框架适配、平台工具等方向划分 |
| 核心职责 | 模块开发与调试、性能优化、接口与工具链维护、与硬件团队协同定位问题 |
| 发展路径 | 软件工程师 → 高级工程师 → 技术专家或技术管理 |
| 能力要求 | 数据结构与算法、C 与 C++ 基础、操作系统与并发、性能与复杂度分析 |
- 面试以白板或共享文档写代码为主,边写边讲比一次写对更重要,卡住时说出思路也能得分
- 每道题几乎都会追问复杂度与边界,答完代码要主动补一句时间空间开销
1.3招聘概况
校招面向计算机、软件工程、电子、自动化、数学等专业的本科、硕士与博士毕业生,岗位分布在上海、北京、深圳等地。流程一般是网申、在线笔试、两到三轮技术面、综合面与录用,技术面以算法与基础为主,部分方向会加问操作系统、网络或体系结构。具体岗位与批次以公司当年校园招聘公告为准。
| 招聘对象 | 应届本科、硕士、博士毕业生 |
| 面向专业 | 计算机科学与技术、软件工程、电子信息、自动化、数学等 |
| 工作地点 | 上海、北京、深圳等 |
| 投递窗口 | 以公司当年校园招聘公告为准 |
企业概况整理自公司官网、公开年报与招聘公告,具体数据以官方最新发布为准。
二、面试总体概述
2.1案例背景
英伟达软件工程师的校招技术面,题目集中在数据结构与算法基本功:字符串排列、数组去零、二叉树遍历与统计、区间合并、链表反转、排序实现。一共十一题,没有偏门算法,但每一题都会被追问边界条件、时间空间复杂度和「还有没有别的解法」。写得出代码只是入场券,讲得清为什么这么写、什么输入会让它出错,才是评分的重点。
2.2基本信息
| 公司 | 英伟达半导体科技(上海)有限公司 |
| 岗位 | 软件工程师 |
| 招聘类型 | 校招 |
| 用工形式 / 部门 | 直招 · 软件研发 |
| 工作地点 | 上海 |
| 面试形式 | 线上 |
| 面试轮次 | 1 轮 |
| 平均时长 | 45 分钟 |
| 题目数量 | 共 11 题,均附参考答案 |
| 整体难度 | ★★★★☆ 偏难 |
| 候选人背景 | 学历 硕士 | 专业 计算机科学与技术 | 经历 有编译器相关课题与两段后端开发实习 |
2.3高频考点
| 考点 | 分类 | 出现轮次 | 次数 |
|---|---|---|---|
| 字符串与数组的回溯、双指针 | 专业基础 | 技术面 | 2 |
| 二叉树的遍历、统计与结构变换 | 专业基础 | 技术面 | 5 |
| 排序与区间合并的贪心思路 | 专业基础 | 技术面 | 2 |
| 链表指针操作与边界处理 | 专业基础 | 技术面 | 1 |
| 重复元素查找的多解法比较 | 专业基础 | 技术面 | 1 |
2.4备考建议
| 优先级 | 易失分点 | 准备动作 |
|---|---|---|
| P0 | 代码能跑但说不清复杂度 | 每道题写完强制自己说三句:时间复杂度、空间复杂度、最坏输入长什么样 |
| P0 | 边界条件靠运气 | 固定检查四件事:空输入、单元素、全相同、极值溢出,做成写码后的口头清单 |
| P1 | 只会一种解法 | 每道经典题准备两个解,比如排列的回溯与字典序、重复数字的求和与快慢指针 |
| P1 | 递归写法会,迭代写不出 | 把二叉树三种遍历的迭代版各手写三遍,理解显式栈的作用 |
| P2 | 不知道面试官在等什么 | 练「先说思路、再确认、后动手」的节奏,把澄清输入输出变成习惯 |
| P2 | 项目讲不出取舍 | 给每个项目准备三个「为什么」:为什么用这个结构、为什么这么分层、瓶颈在哪 |
三、面试流程
3.1流程总览
| # | 环节 | 形式 | 时长 | 筛选重点 |
|---|---|---|---|---|
| 1 | 网申投递 | 公司招聘官网 | — | 学校专业、项目与方向匹配 |
| 2 | 在线笔试 | 在线编程 | 60~90 分钟 | 编码速度、边界处理与复杂度意识 |
| 3 | 技术面 | 线上一对一 | 45~60 分钟 | 算法实现、复杂度分析、代码质量 |
| 4 | 交叉面或综合面 | 线上一对一 | 45 分钟 | 项目深度、系统设计、协作与动机 |
| 5 | 录用与沟通 | 电话或邮件 | — | 岗位方向、薪资与到岗时间 |
3.2各环节说明
英伟达的岗位按方向和团队发布,投递时要看清是驱动、编译器、框架还是平台工具。简历里最好有一条能体现深度:比如自己写过一个解释器、做过 CUDA 优化、维护过开源项目。泛泛写「熟悉 C++」没有区分度。
- 项目写清你负责的模块和技术难点
- 有 GitHub 或论文的,放链接比写「精通」有效
笔试通常是两到四道编程题,难度从简单到中等,语言不限。评分不只看通过用例,也看代码结构和是否处理了空输入、单元素、溢出这些边界。做题顺序建议先扫一遍,把最有把握的两道先拿满。
- 先写暴力解再优化,别一上来追求最优
- 提交前手动跑一遍空数组和单元素
技术面在共享文档上写代码,面试官会给题、看你写、随时打断追问。常见节奏是先让你说思路,再写,再问复杂度,最后加一个约束条件让你改。写不出来时把思路讲清楚,比沉默硬写要好。
- 写代码前先用两句话确认输入输出和约束
- 每写完一个函数,主动说一句它的复杂度
这一轮可能由另一个团队面试官来面,问题会从算法转到项目:为什么这么设计、瓶颈在哪、如果数据量涨十倍怎么办。也可能问操作系统、并发、内存这些基础。回答要落到你做过的具体决定上。
- 准备一个能讲十五分钟的项目,含取舍
- 被问到不会的,说清你的推理路径
通过后进入录用沟通,确认团队方向、职级、薪资结构与到岗时间。外企的录用流程通常需要多轮审批,周期偏长,期间可以主动跟进进度。签约前问清试用期考核方式与是否有轮岗。
- 问清具体团队和汇报线
- 确认 offer 的组成,base 与奖金比例分开看
四、第 1 轮:技术面
4.1面试形式与时长
| 轮次名称 | 技术面 |
| 面试类型 | 技术面 |
| 时长 | 45~60 分钟 |
| 形式 | 线上 |
| 本轮题量 | 11 题 |
4.2重点考查维度
- 专业基础 —— 本组 11 道题
按题型归并为单轮:十一题以数据结构与算法为主,面试官逐题给出、现场编写并追问复杂度与边界,部分题目会临时增加约束条件。
考察回溯框架、去重处理与两种解法的取舍;考察双指针的原地操作与顺序保持;考察递归边界与遍历的空间代价
4.3面试真题与参考答案
先问清:是否有重复字符、是否按字典序输出。
- 回溯:固定第 i 位,与后面每个字符交换,递归 i+1 后换回,本层用集合去重。
- 迭代:字典序法,先排序,反复找最右升序对、交换、反转后缀,天然有序。
- 复杂度:结果 n! 个,时间 O(n·n!),栈深 O(n)。
def permute(s):
res=[]
def dfs(i):
if i==len(s): res.append(''.join(s)); return
for j in range(i,len(s)):
s[i],s[j]=s[j],s[i]; dfs(i+1); s[i],s[j]=s[j],s[i]
dfs(0); return res去重版在循环前加 seen 判断即可。
边界:空数组、全零、无零;是否要求原地修改,这决定空间复杂度。
- 双指针:j 指向待填位置,i 遍历数组,遇到非零就交换并让 j 自增,零被挤到末尾且非零顺序不变。
- 复杂度:时间 O(n),空间 O(1);开新数组的写法更简单,但空间 O(n),要先问是否允许。
def move_zeroes(a):
j=0
for i in range(len(a)):
if a[i]!=0:
a[j],a[i]=a[i],a[j]; j+=1
return a若题目不要求保序,可用首尾指针减少交换次数。
先定义:叶子是左右子节点都为空的节点,空树返回 0。
- 递归:空返回 0,左右都空返回 1,否则返回左右子树之和。
- 迭代:用栈做深度优先,遇叶子计数,避免深树爆栈。
- 复杂度:时间 O(n),空间 O(h),链状最坏 O(n)。
def leaves(root):
if not root: return 0
if not root.left and not root.right: return 1
return leaves(root.left)+leaves(root.right)若追问「第 k 层的叶子」,把深度一起传下去即可。
先确认:输入是否按起点有序、端点相接算不算重叠,这决定边界写法。
- 按起点排序后,后一个区间只可能与结果末尾重叠;重叠就把终点取较大值,否则另起一段。
- 复杂度:排序 O(n log n),合并 O(n),结果数组空间 O(n)。
def merge(iv):
if not iv: return []
iv.sort(key=lambda x:x[0]); res=[iv[0][:]]
for s,e in iv[1:]:
if s<=res[-1][1]: res[-1][1]=max(res[-1][1],e)
else: res.append([s,e])
return res输入本身有序时可省掉排序,总时间降到 O(n)。
先说清返回新头节点;空链表和单节点要能直接返回。
- 三指针:每步先暂存 next,再把 cur.next 指向 prev,最后 prev 与 cur 同步后移,prev 即新头。
- 递归:先反转后面的部分,再让 head.next.next 指回 head,并把 head.next 置空。
- 复杂度:时间 O(n);迭代空间 O(1),递归 O(n)。
def reverse(h):
prev,cur=None,h
while cur:
nxt=cur.next; cur.next=prev; prev,cur=cur,nxt
return prev易错点:忘记暂存 next 会断链,忘记置空会成环。
前提:原地、升序、稳定;长度小于 2 直接返回。
- 外层跑 n-1 轮,内层相邻比较交换,每轮把最大值送到末尾。
- 优化:加 swapped 标志,某轮无交换就提前结束,最好降到 O(n)。
- 复杂度:平均与最坏 O(n²),最好 O(n),空间 O(1),稳定。
def bubble(a):
for i in range(len(a)-1):
ok=True
for j in range(len(a)-1-i):
if a[j]>a[j+1]:
a[j],a[j+1]=a[j+1],a[j]; ok=False
if ok: break
return a大数据量工程上用快排或归并,这题考基本功。
后序是左右根,递归和迭代都要会写。
- 递归:先递归左子树,再右子树,最后访问根。
- 迭代:按根右左的顺序入栈遍历,结果整体反转,就得到左右根。
- 复杂度:时间 O(n),递归空间 O(h),迭代最坏 O(n)。
def postorder(root):
res,st=[],[root] if root else []
while st:
n=st.pop(); res.append(n.val)
if n.left: st.append(n.left)
if n.right: st.append(n.right)
return res[::-1]若追问「不反转结果怎么写」,用 prev 指针模拟调用栈。
先问:是否原地改指针、是否要循环链表。
- 中序即升序,遍历时用 last 记前驱,last.right 指当前、当前.left 指 last。
- 结束后若做循环链表再首尾相接,否则头.left 与尾.right 置空。
- 复杂度:时间 O(n),空间 O(h)。
def to_list(root):
first=last=None
def dfs(n):
nonlocal first,last
if not n: return
dfs(n.left)
if last: last.right,n.left=n,last
else: first=n
last=n; dfs(n.right)
dfs(root); return first改指针前没暂存右子树,遍历会断。
先问条件:值域是 1 到 n 还是 1 到 100、只有一个重复还是多个、能否改数组,这三点决定解法。
- 求和:总和减去 1 到 n 之和,时间 O(n)、空间 O(1),但只在「恰好一个重复、其余各出现一次」时成立。
- 快慢指针:下标当指针、值当跳转,环的入口即重复数,时间 O(n)、空间 O(1),要求值域在 1 到 n。
- 哈希或取负标记:通用性最好,代价是 O(n) 空间或修改原数组。
def dup(a):
s=f=0
while True:
s=a[s]; f=a[a[f]]
if s==f: break
s=0
while s!=f: s,f=a[s],a[f]
return s多个重复时只能用哈希或排序。
先对齐定义:高度按节点数还是边数算,空树是 0 还是 -1,不同定义答案差一。
- 递归:空返回 0,否则左右子树较大值加一,代码最短。
- 迭代:层序遍历,每处理完一层计数加一。
- 复杂度:时间 O(n);递归空间 O(h),层序空间最坏 O(n)。
def height(r):
if not r: return 0
return 1+max(height(r.left),height(r.right))若追问「判断是否平衡」,改成后序一次遍历同时返回高度与标记,避免 O(n²)。定义先对齐。
先确认结构能否改变:要求完全二叉树时,中序取升序、反转、按层序填充。
- 中序得升序数组,反转即降序,再按 2i+1、2i+2 建树,父下标小于子下标,父值必不小于子值。
- 时间与空间均 O(n)。
def to_heap(root):
v=[]
def ino(n):
if n: ino(n.left); v.append(n.val); ino(n.right)
ino(root); v.reverse()
def build(i):
if i>=len(v): return None
n=TreeNode(v[i])
n.left,n.right=build(2*i+1),build(2*i+2)
return n
return build(0)形状必须保留时,只能自底向上交换值。
五、注意事项
英伟达的技术面节奏很紧,一题接一题,评分看的是过程而不是最终代码。以下八处是最常见的失分点。
空输入、单元素、重复值、值域范围,这些不问清就动手,写完一跑就挂。开场两句话确认条件,能省掉一半返工。
面试官问「还有别的思路吗」时卡住,说明准备停在背题层面。每题至少准备暴力解与最优解两个版本。
把全排列说成 O(n!)、把排序说成 O(n),一个数字就能暴露基础不牢。写完主动报时间和空间,并说明最坏情况。
递归写得漂亮但栈深 O(n),在深链上会爆。要说清递归深度,必要时给迭代版。
链表反转忘记置空、树转链表忘记保存头节点,都是典型的收尾遗漏。写完回头扫一遍指针和返回值。
求和法找重复数只在「恰好一个重复」时成立,不声明前提就用,追问一句就崩。
写不出来时一句话不说,面试官无法给分。把思路、卡点、你想尝试的方向说出来,过程本身就是评分项。
写完不跑样例,等于把验证交给面试官。至少手动走一遍最简单的输入和一个边界输入。
总结收尾
这十一题没有一道是偏门算法,全部是数据结构课本上的经典题。真正的分水岭在于:你有没有在写之前确认条件,写完之后说清复杂度和边界,被追问时能不能给出第二种解法。把这些变成肌肉记忆,比多刷一百道难题更有效——面试官要判断的是你写代码时的思维习惯,而不是你背过多少题。
在此推荐你使用求职精灵(https://finsight.work/)进行系统备考。平台拥有英伟达等数百家知名企业的真实面经资源,独家支持中英双语 AI 模拟面试,可针对软件工程师进行定制化练习和智能评分优化;简历智能修改功能帮你打磨出一份亮眼的简历;超过 15 万道行测、专业题、英语题随时随地在线刷题。无论你是第一次校招还是已经面过几轮,求职精灵都能为你的求职之路提供有力支持。