制造焦虑:打破幻觉
最近冲了 Uber 2026 OA,平台依旧是 CodeSignal,题量 4 道题,70 分钟完成。
说实话,这次题目对于有一定基础的同学来说,熟悉度很高,我自己花了 22 分钟就全部做完。四题里有两个题几乎是白送的,看一眼就能写出来,完全没有难度。
为了帮助大家提前练手,我把每道题的思路和做法都整理出来了。
Uber 2026 OA 面试概览
| 项目 | 详情 |
|---|---|
| 平台 | CodeSignal |
| 题量 | 4 道 |
| 时间 | 70 分钟 |
| 难度 | 中等偏上,但部分题目熟悉即可快速完成 |
考察点
- 二进制运算与位操作
- 栈与单调栈技巧
- 子数组与排列判断
- 字符串操作与交换匹配逻辑
总体来说,Uber OA 更看重逻辑清晰度、思路正确性和实现速度,算法难度不是很大,但细节处理容易出错。
Q1: Minimum Operations to Reduce Binary Number to Zero
🔍 题目描述
给定一个正整数 n,每次操作可以加或减 2^i(i ≥ 0),求将 n 变为 0 的最少操作次数。
📝 解题思路
- 将问题看作二进制处理
- 从低位到高位处理每一位,同时维护进位
- 当前位值 = 原位值 + 进位
- 若值为 1:可以直接减法消掉,操作数 +1,或者加法产生进位
- 若值为 2(位 1 + 进位 1):直接进位,不增加操作
- 累加操作数
- 最终进位为 0 时结束
💡 关键洞察
- 二进制中连续的 1 可以通过「加 1 进位」然后「减 1」来优化操作数
- 例如:
111(7) → 加 1 变成1000(8) → 减 8,共 2 次操作 - 而直接减:
111→ 减 4 → 减 2 → 减 1,需要 3 次操作
🧑💻 代码实现
def min_operations_to_zero(n: int) -> int:
"""
Uber OA 真题:二进制数变为 0 的最少操作次数
Args:
n: 正整数
Returns:
int: 最少操作次数
"""
operations = 0
carry = 0
while n > 0 or carry > 0:
# 当前位的值 = 原位值 + 进位
current_bit = (n & 1) + carry
if current_bit == 1:
# 值为 1:需要一次操作(加或减)
# 贪心:如果下一位也是 1,选择加法(产生进位,可能合并连续 1)
next_bit = (n >> 1) & 1
if next_bit == 1:
# 加法:产生进位
carry = 1
else:
# 减法:不产生进位
carry = 0
operations += 1
elif current_bit == 2:
# 值为 2:直接进位,不增加操作
carry = 1
else:
# 值为 0:无需操作
carry = 0
n >>= 1
return operations
# 测试用例
print(min_operations_to_zero(7)) # 7 = 111 -> +1 = 1000 -> -8 = 0, 答案: 2
print(min_operations_to_zero(5)) # 5 = 101 -> -4 = 1 -> -1 = 0, 答案: 2
print(min_operations_to_zero(1)) # 1 = 1 -> -1 = 0, 答案: 1
print(min_operations_to_zero(15)) # 15 = 1111 -> +1 = 10000 -> -16 = 0, 答案: 2
print(min_operations_to_zero(10)) # 10 = 1010 -> -8 = 2 -> -2 = 0, 答案: 2
⚠️ 小技巧
- 二进制位处理题最关键是进位维护
- 一定要从低位向高位遍历,否则操作数计算会出错
- 可以画出二进制位图快速理解操作逻辑
Q2: Discounted Price Sum
🔍 题目描述
每个物品售价 = 原价 − 右边第一个 ≤ 当前价格的物品的价格(若无则按原价出售)。
求总售价及按原价出售物品的索引。
📝 解题思路
- 从右向左遍历物品
- 使用单调递增栈寻找右边第一个 ≤ 当前价格的物品
- 栈空 ⇒ 原价出售,记录索引
- 栈非空 ⇒ 售价 = 原价 − 栈顶价格
- 累加总售价
- 输出总售价及原价出售的物品索引(升序)
🧑💻 代码实现
def discounted_price_sum(prices: list) -> tuple:
"""
Uber OA 真题:折扣价格总和
Args:
prices: 物品原价列表
Returns:
tuple: (总售价, 原价出售的索引列表)
"""
n = len(prices)
total = 0
full_price_indices = []
stack = [] # 单调递增栈,存储 (价格, 索引)
# 从右向左遍历
for i in range(n - 1, -1, -1):
current_price = prices[i]
# 弹出所有大于当前价格的元素
while stack and stack[-1][0] > current_price:
stack.pop()
if not stack:
# 栈空:原价出售
total += current_price
full_price_indices.append(i)
else:
# 栈非空:折扣价 = 原价 - 右边第一个 ≤ 当前价格的物品价格
discount_price = current_price - stack[-1][0]
total += discount_price
# 当前元素入栈
stack.append((current_price, i))
# 索引升序排列
full_price_indices.sort()
return total, full_price_indices
# 测试用例
prices1 = [8, 4, 6, 2, 3]
print(discounted_price_sum(prices1))
# 分析:
# i=4: 3, 栈空, 原价 3
# i=3: 2, 栈空, 原价 2
# i=2: 6, 栈顶 2 ≤ 6, 售价 6-2=4
# i=1: 4, 栈顶 2 ≤ 4, 售价 4-2=2
# i=0: 8, 栈顶 4 ≤ 8, 售价 8-4=4
# 总价: 4+2+4+2+3 = 15, 原价索引: [3, 4]
prices2 = [5, 1, 4, 2]
print(discounted_price_sum(prices2))
⚠️ 小技巧
- 单调栈是处理「右边第一个符合条件元素」的经典工具
- 这类题核心是顺序和栈状态维护
- 输出索引前注意排序,否则可能 WA
Q3: Balanced Subarray
🔍 题目描述
对于每个 k(1 ~ n),判断是否存在一个子数组恰好是 1 ~ k 的排列。
📝 解题思路
- 先记录每个数字在排列中的位置
pos[value] - 从
k = 1开始,维护包含数字1 ~ k的最小位置区间[L, R]L = min(pos[1..k])R = max(pos[1..k])
- 如果
R − L + 1 == k,则k是 balanced- 区间长度恰好等于 k,且值域为 1 ~ k,必为排列
- 否则不是 balanced
🧑💻 代码实现
def balanced_subarray(arr: list) -> list:
"""
Uber OA 真题:判断 1~k 是否存在 balanced 子数组
Args:
arr: 1~n 的排列
Returns:
list: 长度为 n 的布尔数组,result[i] 表示 k=i+1 是否 balanced
"""
n = len(arr)
# 记录每个数字的位置(1-indexed value -> 0-indexed position)
pos = [0] * (n + 1)
for i, val in enumerate(arr):
pos[val] = i
result = []
min_pos = float('inf')
max_pos = float('-inf')
for k in range(1, n + 1):
# 更新包含 1~k 的区间范围
min_pos = min(min_pos, pos[k])
max_pos = max(max_pos, pos[k])
# 判断区间长度是否恰好为 k
interval_len = max_pos - min_pos + 1
if interval_len == k:
result.append(True)
else:
result.append(False)
return result
# 测试用例
arr1 = [3, 1, 2, 5, 4]
print(balanced_subarray(arr1))
# k=1: pos[1]=1, 区间[1,1], len=1 ✓
# k=2: pos[2]=2, 区间[1,2], len=2 ✓
# k=3: pos[3]=0, 区间[0,2], len=3 ✓
# k=4: pos[4]=4, 区间[0,4], len=5 ≠ 4 ✗
# k=5: pos[5]=3, 区间[0,4], len=5 ✓
# 输出: [True, True, True, False, True]
arr2 = [2, 1, 4, 3]
print(balanced_subarray(arr2))
# k=1: pos[1]=1, 区间[1,1], len=1 ✓
# k=2: pos[2]=0, 区间[0,1], len=2 ✓
# k=3: pos[3]=3, 区间[0,3], len=4 ≠ 3 ✗
# k=4: pos[4]=2, 区间[0,3], len=4 ✓
# 输出: [True, True, False, True]
⚠️ 小技巧
- 关键是记录数字位置,并维护区间最小值和最大值
- 可以在遍历中累积更新 min/max,无需重复扫描
- 这种题非常适合用数组 + 索引映射实现,时间复杂度 O(n)
Q4: Maximum Pairs by Swapping Digits
🔍 题目描述
两个数字若可通过最多两次交换某些位置的数字变成彼此(含相等),算作一对。求总对数。
📝 解题思路
- 长度不同 ⇒ 不可能配对
- 长度相同:
- 将数字转为字符串并排序字符
- 若排序后相同 ⇒ 可通过若干次交换变成彼此(计数)
- 若排序后不同:
- 统计不同位置的字符
- 若不同位置数 ≤ 4 且字符可两两匹配 ⇒ 可两次交换
- 实现步骤:
- 先按长度分组
- 组内用哈希表统计排序串频次计算相等对数
- 对不等情况检查是否符合两次交换条件
🧑💻 代码实现
from collections import defaultdict
def max_pairs_by_swapping(nums: list) -> int:
"""
Uber OA 真题:最多两次交换配对数
Args:
nums: 数字列表
Returns:
int: 可配对的总对数
"""
# 按长度分组
length_groups = defaultdict(list)
for num in nums:
s = str(num)
length_groups[len(s)].append(s)
total_pairs = 0
for length, group in length_groups.items():
n = len(group)
# 统计排序后相同的字符串(可通过交换变成彼此)
sorted_count = defaultdict(int)
for s in group:
sorted_s = ''.join(sorted(s))
sorted_count[sorted_s] += 1
# 计算同排序串的配对数:C(count, 2) = count * (count - 1) / 2
for count in sorted_count.values():
total_pairs += count * (count - 1) // 2
return total_pairs
def can_match_with_two_swaps(s1: str, s2: str) -> bool:
"""
检查两个字符串是否可以通过最多两次交换变成彼此
"""
if len(s1) != len(s2):
return False
# 找出不同位置
diff_positions = []
for i in range(len(s1)):
if s1[i] != s2[i]:
diff_positions.append(i)
# 0 个不同:已相等
if len(diff_positions) == 0:
return True
# 2 个不同:一次交换
if len(diff_positions) == 2:
i, j = diff_positions
return s1[i] == s2[j] and s1[j] == s2[i]
# 4 个不同:两次交换(需要检查字符是否匹配)
if len(diff_positions) == 4:
chars1 = [s1[i] for i in diff_positions]
chars2 = [s2[i] for i in diff_positions]
return sorted(chars1) == sorted(chars2)
return False
# 更精确的版本(考虑不同位置的交换)
def max_pairs_precise(nums: list) -> int:
"""
精确版本:检查每对是否可通过最多两次交换配对
"""
n = len(nums)
pairs = 0
for i in range(n):
for j in range(i + 1, n):
s1, s2 = str(nums[i]), str(nums[j])
if can_match_with_two_swaps(s1, s2):
pairs += 1
return pairs
# 测试用例
nums1 = [123, 321, 132, 456]
print(max_pairs_by_swapping(nums1)) # 123, 321, 132 可互相配对,共 3 对
nums2 = [111, 111, 111]
print(max_pairs_by_swapping(nums2)) # 3 个相同,C(3,2) = 3 对
nums3 = [12, 21, 34, 43, 56]
print(max_pairs_by_swapping(nums3)) # (12,21), (34,43) = 2 对
⚠️ 小技巧
- 排序字符串可以快速判断是否可能通过交换变成彼此
- 不同位置 ≤ 4 是关键判断条件
- 哈希表分组统计可以大幅减少时间复杂度
复杂度分析
| 题目 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| Q1: Binary to Zero | O(log n) | O(1) |
| Q2: Discounted Price | O(n) | O(n) |
| Q3: Balanced Subarray | O(n) | O(n) |
| Q4: Max Pairs | O(n² × L) 或 O(n × L log L) | O(n × L) |
其中 L = 数字的最大位数
🔥 为什么大多数人会挂?
| 常见错误 | 正确做法 |
|---|---|
| Q1 忘记处理进位 | 维护 carry 变量 |
| Q1 从高位开始处理 | 必须从低位向高位遍历 |
| Q2 单调栈方向错误 | 从右向左遍历 |
| Q2 索引没排序 | 输出前升序排列 |
| Q3 重复扫描求 min/max | 累积更新 min_pos, max_pos |
| Q4 只考虑相等情况 | 还要考虑 2 次和 4 次不同位置 |
FAQ | Uber 2026 OA 常见问题
Q1: Uber 2026 OA 有几道题?难度如何?
Uber 2026 OA 一共有 4 道题,题型包括二进制加减最少操作数、单调栈折扣售价、子数组排列判断和数字最多交换匹配对数。难度偏中等偏上,但大部分题型属于常见模板题型,只要熟悉 CodeSignal 平台和 Python 解题思路,通常可以快速完成。
Q2: 完成 Uber 2026 OA 大概需要多长时间?
根据实战经验,如果熟悉题型,四题可以在 20–30 分钟内完成全部操作。我自己整套题花了 22 分钟就一次过,通过合理规划题目顺序和使用 Python 模板代码,可以显著节省时间。
Q3: Uber 2026 OA 有哪些考察重点?
主要考察以下能力:
- 二进制位运算和进位处理
- 单调栈应用和折扣售价计算逻辑
- 子数组排列判断和区间最小/最大位置维护
- 字符串数字匹配与最多两次交换逻辑
此外,还会考察边界条件处理、算法复杂度理解和代码实现能力。
Q4: Python 在 Uber OA 中有哪些优势?
Python 语法简洁,内置数据结构(如字典、列表、集合)和函数(如 min, max, sorted)非常适合处理 OA 题目逻辑,尤其是:
- 二进制位操作和进位模拟
- 单调栈或栈操作
- 索引映射和子数组判断
- 字符串转换和哈希统计
合理使用 Python 模板代码,可以提高完成速度和正确率。
Q5: 有没有快速通过 Uber OA 的经验技巧?
经验总结如下:
- 熟悉题型模板:二进制位处理、单调栈、区间判断、字符交换匹配
- 先理清逻辑再写代码:画草稿或用小样例测试
- 时间分配合理:先做熟悉题,难题留最后
- 边界条件复盘:尤其是交易次数、进位、索引长度、字符匹配
- 模板代码复用:提前准备好函数/工具方法,可直接调用
Q6: Uber OA 的题目会重复吗?
根据实战经验,CodeSignal 平台上的题目类型重复率较高,尤其是类似二进制操作、单调栈和子数组排列的经典模板题型。提前练习这些题型,理解通用解法和边界处理方法,可以大幅提高 AC 准确率。
📞 oavoservice 服务
别再浪费时间!Uber / TikTok / Stripe OA 快速通关指南。
很多同学其实不是不会写,而是 OA 时间压力太大、容易在细节上翻车。
针对这种情况,我们提供成熟的大厂 OA 支持方案:
- ✅ OA代写:CodeSignal / HackerRank / 牛客网 满分保障
- ✅ 笔试协助:高频题型快速处理
- ✅ 远程无痕操作:过程无痕、执行稳定
- ✅ 目标:帮助你在关键 OA 环节一次到位,减少不必要的消耗
👉 立即添加微信:Coding0201
不要让 4 道 OA 题,毁掉你的 Uber Offer。
本文由 oavoservice 团队原创,转载请注明出处。