Pinterest 2026 面试全流程攻略:OA → Phone → Onsite 真实面经汇总
Pinterest 作为全球领先的视觉发现平台,每年吸引着大量优秀的工程师加入。本文基于 2025–2026 年多位候选人的真实面试经历,系统整理 Pinterest 从 OA 到 Onsite 的完整面试流程、高频编程真题、系统设计考点以及行为面试要点,帮助你全面备战 Pinterest 的技术面试。
无论你是刚拿到 OA 通知的新人,还是准备冲刺 Onsite 的资深候选人,这篇攻略都能为你提供清晰的方向和实用的解题思路。所有题目均附有详细的 Python 代码实现和复杂度分析,确保你不仅能通过面试,还能真正理解每道题背后的核心思想。
一、Pinterest 面试流程全景图
Pinterest 的面试流程相对标准但节奏紧凑,整体分为三个核心阶段。每个阶段都有明确的淘汰标准,提前了解流程结构,可以帮你合理分配精力和时间。
整体流程
Pinterest 的面试路径可以概括为:OA → Phone Screen → Onsite。其中 Onsite 包含 3 轮 Coding、1 轮 System Design 和 1 轮 Behavioral(BQ),共计 5 轮面试。
整个流程通常需要 4–8 周完成,具体取决于招聘节奏和面试官的排期。值得注意的是,Pinterest 的面试难度整体属于中等水平,但 System Design 环节被多位候选人评价为”坑爹”——题目抽象、要求高、面试官期望明确,是整轮面试中最大的难点。
流程阶段与时间节点
第一阶段是 Online Assessment(OA),通常在简历通过筛选后 1–2 周内发放,要求在 90 分钟内完成 2–3 道算法题。第二阶段是 Phone Screen,通过 OA 后 1–2 周内安排,45 分钟的电话视频面试。第三阶段是 Onsite,由 5 轮面试组成,通常集中安排在同一天的 5–6 小时内(虚拟 onsite 也可以分段进行)。
每个阶段之间,Pinterest 的反馈速度相对较快。如果 OA 通过,通常会在 3–5 个工作日内收到 Phone 面试的邀请邮件。Phone 通过后,Onsite 的协调一般也在 1–2 周内完成。
二、Online Assessment(OA)详解
OA 是整个面试流程的第一道门槛,也是最具”秒杀”风险的环节。Pinterest 的 OA 一般通过 Codility 平台进行,包含 2 到 3 道编程题,总时长为 90 分钟。题目类型以中等难度为主,涉及数组操作、字符串处理、贪心算法和动态规划等常见方向。
OA 常见题型
根据 2025–2026 年的候选人反馈,Pinterest 的 OA 题型集中在以下几个方向:字符串分割与解析、二维数组遍历与统计、排序与去重问题、以及简单的图论与路径查找。其中字符串处理题出现的频率最高,几乎每半年都会有一变种。
OA 的评判标准主要包括:代码正确性(能否通过所有隐藏测试用例)、时间复杂度(是否在限定范围内)、代码可读性(命名清晰、结构合理)。值得注意的是,Pinterest 的 OA 测试用例覆盖非常全面,包括各种边界情况——空输入、极端值、特殊字符等,所以”勉强能跑”是不够的,必须考虑到所有 edge cases。
OA 备考策略
首先,熟悉 Codility 的答题环境。它的编辑器支持 Python、Java、C++、JavaScript 等主流语言,但不支持第三方库(除了标准库)。其次,重点练习字符串处理和数组操作类题目——这类题目在 Pinterest 的 OA 中占比超过 60%。
此外,时间管理至关重要。90 分钟面对 3 道题,建议分配策略为:第一题 20 分钟,第二题 35 分钟,第三题 35 分钟。如果某道题卡住超过 15 分钟没有思路,先跳过,完成其他题目后再回来。Codility 的评分机制是累加的,完成两道题的满分通常比只做一道半的题更有优势。
推荐刷题范围:LeetCode 上 Easy 到 Medium 难度的字符串和数组题目,尤其是涉及分割(split)、遍历(traverse)和哈希表(hash map)的题目。建议至少完成 50 道以上相关题目,保证在考试时有足够的”手感”。
三、Phone Screen 电话面试
Phone Screen 通常由一位 Pinterest 的工程师进行,时长约 45 分钟。形式一般为 Google Meet 或 Zoom 视频通话,面试官会通过共享屏幕或在线编辑器与你共同完成 1 到 2 道编程题。
Phone 面试特点
与 OA 不同,Phone Screen 更注重你的思考过程和沟通能力。面试官不会一上来就丢一道难题,而是会先简单自我介绍,然后从一道中等难度的题目开始。在解题过程中,面试官会密切关注你是否能:
第一,清晰地复述题目并确认理解是否正确。第二,先口头阐述解题思路再写代码——这一点在 Phone 面试中比在 OA 中重要得多。第三,在写代码过程中主动讨论时间复杂度和空间复杂度。第四,在完成后能够自己设计测试用例并验证。
Phone 面试真题示例
根据候选人反馈,Phone Screen 的高频题目包括:数组去重排序、最长子串、二分查找变种等。难度通常为 LeetCode Medium,与 OA 的第三题难度相当。面试官有时会在一道大题的基础上追加 follow-up 问题,考察你在已有方案上的优化能力。
Phone 面试的通过标准比较明确:能够独立完成至少一道题目,并且在整个过程中展现出良好的沟通习惯和结构化思维。如果面试官在你写第一道题时没有提出任何 follow-up,这通常是一个积极的信号——说明你对第一道题的解决足够扎实,不需要额外考验。
四、Onsite Coding 真题详解
Onsite 阶段是 Pinterest 面试的核心,包含 3 轮独立的 Coding 面试。每轮约 45 分钟,由不同的工程师出题。题目难度整体为中等,但每轮考察的知识点不同,分别覆盖字符串处理、数据结构设计和算法综合应用三大方向。
下面逐一解析三轮 Coding 面试的高频真题,包括题目描述、思路分析、代码实现和复杂度讨论。
真题一:String Tokenizer(字符串分词器)
这是一道在 Pinterest 面试中出现频率极高的经典题目。题目给你一个类,这个类有一个 next() 方法,每次调用返回一个可能包含换行符(newline)的字符串片段。你需要自己实现一个函数来处理这些带换行符的片段,正确输出完整的 token。
题目描述
假设输入是一个 Tokenizer 类,其 next() 方法依次返回字符串片段。这些片段可能包含 \n 换行符。你的任务是实现一个 read_next_token() 函数,它调用 tokenizer.next() 读取数据,正确处理换行符,最终返回按行分割的完整 token 列表。
举例来说,如果 tokenizer.next() 依次返回 "one\n"、"two",你的函数应该输出 ["one", "two"]。如果返回 "o"、"ne\n"、"two",同样应该输出 ["one", "two"]。关键在于:换行符可能出现在任意片段的任意位置,包括片段末尾、片段中间,甚至可能连续出现多个换行符。
解题思路
核心思想是:用一个缓冲区(buffer)累积所有从 next() 读取到的字符。每读取一个片段,将其追加到缓冲区。然后扫描缓冲区,找到第一个 \n 的位置——该位置之前的内容就是一个完整的 token。将这个 token 取出(注意不要包含 \n 本身),然后继续处理缓冲区中剩余的部分。如果缓冲区中没有 \n,说明当前 token 还没结束,继续从 next() 读取更多片段。
关键的 edge cases 包括:(1) 连续多个 \n 意味着有空行 token(空字符串);(2) 最后一个 token 可能没有 \n 结尾,需要在 next() 返回空字符串时将其作为最后一个 token 输出;(3) split('\n') 的行为需要特别注意——"a\nb\n".split('\n') 返回 ['a', 'b', ''],末尾多一个空字符串。
代码实现
class Tokenizer:
"""模拟的 Tokenizer,next() 返回带换行符的字符串片段"""
def __init__(self, chunks):
self.chunks = chunks
self.index = 0
def next(self):
"""返回下一个字符串片段,没有更多数据时返回空字符串"""
if self.index < len(self.chunks):
result = self.chunks[self.index]
self.index += 1
return result
return ""
def read_all_tokens(tokenizer: Tokenizer) -> list:
"""
从 Tokenizer 读取所有 token(以换行符分隔)。
核心逻辑:
1. 用 buffer 累积所有读取到的字符
2. 每次找到 '\n' 就切出一个完整 token
3. next() 返回空字符串时,把 buffer 剩余部分作为最后一个 token
Edge Cases:
- 连续 \n 产生空 token
- 最后一个 token 可能没有 \n 结尾
- split 会产生额外的空字符串,需要特殊处理
"""
tokens = []
buffer = ""
while True:
chunk = tokenizer.next()
# next() 返回空字符串表示没有更多数据
if not chunk:
# 把 buffer 中剩余的部分作为最后一个 token
if buffer:
tokens.append(buffer)
break
buffer += chunk
# 检查 buffer 中是否有换行符
while '\n' in buffer:
# 找到第一个换行符的位置
newline_pos = buffer.index('\n')
# 取出换行符之前的内容作为 token
token = buffer[:newline_pos]
tokens.append(token)
# 保留换行符之后的内容继续处理
buffer = buffer[newline_pos + 1:]
return tokens
# 测试用例 1:正常情况
t1 = Tokenizer(["one\n", "two"])
print(read_all_tokens(t1)) # 输出: ['one', 'two']
# 测试用例 2:换行符在片段中间
t2 = Tokenizer(["o", "ne\n", "two"])
print(read_all_tokens(t2)) # 输出: ['one', 'two']
# 测试用例 3:连续换行符(空行)
t3 = Tokenizer(["a\n\nb"])
print(read_all_tokens(t3)) # 输出: ['a', '', 'b']
# 测试用例 4:最后一个 token 没有换行符
t4 = Tokenizer(["hello", " ", "world"])
print(read_all_tokens(t4)) # 输出: ['hello world']
# 测试用例 5:只有一个换行符结尾
t5 = Tokenizer(["line1\nline2\n"])
print(read_all_tokens(t5)) # 输出: ['line1', 'line2']
复杂度分析
时间复杂度为 O(N),其中 N 是所有输入片段的总字符数。每个字符最多被处理两次:一次追加到 buffer,一次从 buffer 切出。空间复杂度也是 O(N),最坏情况下(没有换行符),所有字符都存储在 buffer 中。
常见面试官 follow-up
面试官通常会追问:如何处理 \r\n(Windows 风格换行)?如何处理超大输入(GB 级别)?如果要求只返回非空 token 怎么做?这些都是考察你对边界情况处理能力和工程思维的延伸问题。建议在面试中主动提到这些点,展示你的全面思考。
真题二:电影推荐系统(Movie Recommendation)
这道题模拟了真实的推荐系统场景,考察你处理多条件筛选和哈希表操作的能力。题目给定一个二维字符串数组,每个元素是 [user, movie, rating] 的格式,要求你为指定用户推荐符合条件的电影列表。
题目描述
输入是一个 ratings 数组,其中每个元素为 [user_id, movie_name, rating],rating 为 1–5 的整数。给定一个目标用户 target_user,返回推荐电影列表。推荐电影需要同时满足以下三个条件:
条件一:目标用户没有看过该电影。条件二:至少有一个其他用户看过该电影,并且给出的 rating 大于 3。条件三:存在至少一个”共同用户”,该用户和目标用户都看过同一部电影,并且两个人对那部电影的 rating 都大于 3。这个”共同用户”就是给推荐电影打了高分的那位。
解题思路
分三步走。第一步,建立三个数据结构:目标用户看过的电影集合、每个用户看过的电影到 rating 的映射、每部电影被哪些用户以高评分(>3)推荐过。第二步,找出所有与目标用户”口味相似”的用户——即与目标用户共同看过某部电影且双方都给了 >3 评分的用户。第三步,从这些相似用户看过的电影中,筛选出目标用户没看过且评分 >3 的电影作为推荐结果。
代码实现
from collections import defaultdict
def recommend_movies(ratings: list, target_user: str) -> list:
"""
为指定用户推荐电影。
条件:
1. 目标用户没有看过该电影
2. 至少一个其他用户看过且 rating > 3
3. 该其他用户与目标用户共同看过某部电影
且两人对该电影的 rating 都 > 3
Args:
ratings: [[user, movie, rating], ...]
target_user: 目标用户ID
Returns:
推荐电影列表
"""
# 第一步:构建数据结构
# user -> {movie: rating}
user_movies = defaultdict(dict)
# movie -> {user: rating}
movie_users = defaultdict(dict)
for user, movie, rating in ratings:
rating = int(rating)
user_movies[user][movie] = rating
movie_users[movie][user] = rating
# 目标用户看过的电影
target_watched = set(user_movies.get(target_user, {}).keys())
# 第二步:找出与目标用户口味相似的"共同用户"
# 共同用户 = 与目标用户至少共同看过一部电影
# 且两人对该电影的 rating 都 > 3
similar_users = set()
for movie, target_rating in user_movies.get(target_user, {}).items():
if target_rating <= 3:
continue # 目标用户评分 <= 3,跳过
# 检查其他用户对该电影的评分
for other_user, other_rating in movie_users[movie].items():
if other_user == target_user:
continue
if other_rating > 3:
similar_users.add(other_user)
# 第三步:从相似用户看过的电影中筛选推荐
recommendations = set()
for similar_user in similar_users:
for movie, rating in user_movies.get(similar_user, {}).items():
if int(rating) > 3 and movie not in target_watched:
recommendations.add(movie)
return sorted(list(recommendations))
# 测试用例
ratings_data = [
["Alice", "MovieA", "5"],
["Alice", "MovieB", "4"],
["Alice", "MovieC", "2"],
["Bob", "MovieA", "4"],
["Bob", "MovieD", "5"],
["Bob", "MovieE", "3"],
["Charlie", "MovieB", "5"],
["Charlie", "MovieF", "4"],
["Charlie", "MovieA", "2"],
]
# Alice 看过 MovieA(5), MovieB(4), MovieC(2)
# Bob 与 Alice 都看过 MovieA,Alice给5>3,Bob给4>3 → Bob是相似用户
# Charlie 与 Alice 都看过 MovieB,Alice给4>3,Charlie给5>3 → Charlie是相似用户
# Bob 推荐: MovieD(5>3, Alice没看过) ✓
# Charlie 推荐: MovieF(4>3, Alice没看过) ✓
# Bob 的 MovieE rating=3,不满足 >3 → 不推荐
# Charlie 的 MovieA Alice 看过了 → 排除
print(recommend_movies(ratings_data, "Alice"))
# 输出: ['MovieD', 'MovieF']
复杂度分析
设用户数为 U,电影数为 M,评分记录数为 R。构建哈希表的时间为 O(R)。找相似用户需要遍历目标用户看过的每部电影(最多 M 部)以及其他用户的评分记录,最坏 O(M × U)。筛选推荐同样最多 O(M × U)。总体时间复杂度为 O(R + M × U),空间复杂度为 O(R) 用于存储两个哈希表。
面试官关注点
面试官会关注你是否正确理解了三个条件的组合关系——特别是条件三中的”共同用户”概念,以及它如何与条件二关联。此外,面试官可能会追问:如果有大量用户怎么办?推荐结果是否需要排序(按评分高低、按相似用户数量)?这些都是向真实推荐系统靠齐的延伸问题。
真题三:Connection Count 用户分组
这道题考察的是 HashMap 的基本应用。给定一组用户和他们各自的 connection count(连接数量),要求你根据给定的阈值 N,将所有用户分为两类:connection count 小于 N 的用户和大于等于 N 的用户。
题目描述
输入是一个用户列表,每个用户包含用户名和 connection count。给定一个整数 N,返回两个列表:一个包含 connection count < N 的用户,另一个包含 connection count >= N 的用户。要求使用 HashMap 实现。
代码实现
from collections import defaultdict
def group_users_by_connections(users: list, threshold: int) -> dict:
"""
根据 connection count 将用户分为两组。
Args:
users: [(username, connection_count), ...]
threshold: 分界值 N
Returns:
{
"below": [connection_count < N 的用户列表],
"above_or_equal": [connection_count >= N 的用户列表]
}
"""
# 使用 HashMap 存储用户信息
user_map = {}
for username, count in users:
user_map[username] = count
below = []
above_or_equal = []
for username, count in user_map.items():
if count < threshold:
below.append(username)
else:
above_or_equal.append(username)
return {
"below": sorted(below),
"above_or_equal": sorted(above_or_equal)
}
# 进阶版:支持动态添加/更新用户
class ConnectionTracker:
"""使用 HashMap 管理用户连接数的动态系统"""
def __init__(self):
# user_name -> connection_count
self.user_connections = {}
def add_user(self, username: str, count: int):
"""添加或更新用户连接数"""
self.user_connections[username] = count
def remove_user(self, username: str):
"""移除用户"""
if username in self.user_connections:
del self.user_connections[username]
def get_groups(self, threshold: int) -> dict:
"""按阈值分组"""
below = []
above_or_equal = []
for username, count in self.user_connections.items():
if count < threshold:
below.append(username)
else:
above_or_equal.append(username)
return {
"below": sorted(below),
"above_or_equal": sorted(above_or_equal)
}
def get_connection_count(self, username: str) -> int:
"""查询用户的连接数"""
return self.user_connections.get(username, 0)
# 测试
tracker = ConnectionTracker()
tracker.add_user("Alice", 150)
tracker.add_user("Bob", 50)
tracker.add_user("Charlie", 200)
tracker.add_user("David", 75)
tracker.add_user("Eve", 100)
result = tracker.get_groups(100)
print(result)
# 输出:
# {
# "below": ["Bob", "David"],
# "above_or_equal": ["Alice", "Charlie", "Eve"]
# }
面试官 follow-up 方向
面试官可能会追问:如果用户量达到百万级,HashMap 的性能如何?如何处理并发写入?如果需要按 connection count 排序输出怎么办?如果阈值 N 是动态变化的(需要多次查询不同阈值),如何优化?进阶答案包括:使用桶排序预处理、使用有序数据结构(如 TreeMap / SortedList)、或者使用布隆过滤器快速判断某个用户是否存在。
五、System Design 系统设计面试
根据多位候选人的真实反馈,Pinterest 的 System Design 面试是整个 Onsite 过程中最具挑战性的环节,甚至被评价为”坑爹”。这并非因为题目本身有多难,而是因为 Pinterest 对系统设计的考察方式比较特殊——面试官往往会给出一个相对开放但要求极高的设计题目,期望候选人在有限时间内做出完整且可扩展的架构设计。
SD 面试特点
Pinterest 的 SD 面试通常持续 45 分钟,面试官会给出一个与 Pinterest 业务相关的系统设计题目。常见方向包括:设计一个图片/视频推荐系统、设计 Pinterest 的 Pin feed(信息流)、设计一个大规模图片搜索引擎、或者设计一个 Pin 的保存和分享系统。
与其他公司不同,Pinterest 的 SD 面试有几个显著特点:第一,面试官对”深度”要求很高——不仅要求你画出架构图,还要求你解释每个组件的具体技术选型和取舍原因。第二,面试官会不断追问边界情况,比如”如果 QPS 从 1000 增加到 100 万怎么办””如果某个节点挂了,系统如何保证可用性”。第三,面试官非常关注数据一致性问题和缓存策略,尤其是 Pinterest 作为一个内容密集型平台,缓存的一致性和更新策略是必考话题。
SD 备考框架
面对 Pinterest 的 SD 面试,建议采用以下结构化框架来组织你的回答:
第一步:需求澄清(5 分钟)。确认功能需求(API 接口、核心操作)和非功能需求(QPS、延迟、可用性、数据量)。不要跳过这一步——Pinterest 的面试官非常看重这一点。
第二步:高层架构设计(10 分钟)。画出核心组件:客户端 → CDN → Load Balancer → API Server → 业务服务 → 数据库/缓存。Pinterest 的业务特点决定了 CDN 和图片存储(S3)是必提的组件。
第三步:详细设计(15 分钟)。深入每个组件:数据库选型(MySQL vs Cassandra vs DynamoDB)、缓存策略(Redis 的 cache-aside vs write-through)、消息队列(Kafka for feed generation)、搜索方案(Elasticsearch)。对于 Pinterest 这样的平台,Feed 生成通常采用 Fan-out-on-write 策略,而图片推荐则需要结合协同过滤和深度学习模型。
第四步:瓶颈分析与优化(10 分钟)。主动讨论系统的瓶颈:数据库读写压力、缓存一致性、CDN 回源率、冷启动问题。Pinterest 面试官特别喜欢追问”如何保证数据一致性”和”如何设计缓存淘汰策略”这两个问题,务必提前准备。
SD 高频考点清单
Pinterest 的 SD 面试高频考点包括:(1) 大规模图片/视频存储与分发(CDN 策略、多分辨率图片生成、缩略图缓存);(2) Feed 系统(Fan-out-on-write vs Fan-out-on-read 的权衡、混合 feed 的设计);(3) 推荐系统(协同过滤、内容推荐、实时 vs 离线推荐 pipeline);(4) 搜索系统(倒排索引、向量相似度搜索、Elasticsearch 调优);(5) 数据一致性(分布式事务、最终一致性、缓存失效策略);(6) 负载均衡与水平扩展(服务拆分、数据库分片、读写分离)。
建议重点阅读 《Designing Data-Intensive Applications》前 8 章,并参考 Grokking the System Design Interview 中与 Pinterest 相关的题目。实际面试时,画图的清晰度同样重要——建议练习在白板上画出清晰的分层架构图,标注数据流向和每个组件的职责。
六、Behavioral 行为面试(BQ)
Pinterest 的 BQ 面试由一位 Senior 工程师或工程经理进行,约 30–45 分钟。虽然不考察技术能力,但这一轮对最终结果的影响不容忽视——多位候选人反馈,BQ 面试是他们被拒或被接受的”临门一脚”。
BQ 高频问题
Pinterest 的 BQ 面试主要围绕以下几个核心主题展开。第一类是协作与冲突管理:”描述一次你与团队成员意见分歧的经历,你是如何处理的?””你如何在跨团队合作中推动一个项目?”第二类是技术领导力:”你主导过的最有技术挑战性的项目是什么?””你如何指导 junior 工程师成长?”第三类是个人动机:”为什么选择 Pinterest?””Pinterest 的产品中你最欣赏哪个功能?”第四类是失败与成长:”描述一次你犯的重大错误以及你从中学到了什么?”
STAR 法则应用
回答 BQ 问题时,严格使用 STAR 法则:Situation(背景)、Task(任务)、Action(行动)、Result(结果)。Pinterest 面试官尤其关注 Action 部分——你具体做了什么、为什么这么做、有没有其他备选方案。Result 部分要有可量化的成果,比如”将 API 响应时间从 500ms 降低到 80ms”或”团队交付效率提升了 30%”。
提前准备 5–6 个核心故事,覆盖不同的主题(技术挑战、团队协作、领导力、冲突解决、失败经验),并确保每个故事都能用 STAR 法则清晰呈现。面试时根据面试官的问题灵活调整故事的侧重点,但不要临时编造——Pinterest 面试官通常会根据你的回答进行深度追问,编造的故事很难经得起连续追问。
七、总结与备考建议
Pinterest 2026 的面试流程整体难度中等,但有几个环节需要你特别重视。首先是 OA 阶段的字符串处理题——这是 Pinterest 最偏爱的题型,务必在刷题时给予足够权重。其次是 Onsite 的三轮 Coding,涵盖了字符串解析、哈希表操作和推荐系统算法,难度分布均匀但要求代码质量和 edge case 覆盖都很高。最重要的是 System Design 环节——这是多位候选人公认的最大难点,需要你在架构设计、技术选型和深度追问三个层面都有扎实的准备。
以下是具体的备考时间分配建议:如果距离面试还有 4 周,前 2 周集中攻克 LeetCode 字符串、数组和哈希表相关题目(每天 3–5 道),后 1 周专攻 System Design(每天 1 个设计题),最后 1 周进行 Mock Interview 和 BQ 故事准备。如果时间更充裕(6–8 周),可以在前两周加入动态规划和图论的练习,Pinterest 的 OA 偶尔也会涉及这些方向。
最后提醒几点实战经验:第一,面试中遇到不会的题不要慌——Pinterest 面试官更看重你的思考过程而非最终答案。主动说出你的思路、提出你的假设、讨论你的 trade-off,这些比沉默地写代码更有价值。第二,Code Review 习惯很重要——写完代码后主动检查一遍边界情况,这在面试中能给你额外加分。第三,面试结束前,主动问面试官 1–2 个有深度的问题,比如团队的技术栈、当前面临的技术挑战等,这能展示你对职位的热情和认真态度。