找重复数字,用set就结束了?这道校招题还能追问三层
先看这组数:7、3、7、3、7。
如果题目说“找出重复数字”,你会返回什么?7和3?每个数各出现几次?还是所有重复出现的位置?
这几种答案都可能符合某一种需求,但它们不是同一个函数。
用一道模拟面试题往下拆,最值得展示的第一项能力,就是在动手写代码前,把“重复”的输出定义问清楚。
第一层:你求的是重复的值,还是重复的次数
对刚才的输入,几个结果分别是:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
直接写set只会得到不同的值,无法单靠这个结果知道哪些值重复。len(values) - len(set(values))能计算多出来的出现次数,也不能告诉你每个值重复了几次
先约定本次函数:输入只接受整数,不把布尔值算作整数;输出所有出现超过一次的值,按它们在输入中首次出现的顺序排列。
from collections import Counter
def duplicated_values(values):
counts = Counter()
for value in values:
if type(value) is not int:
raise ValueError("只接受整数,不接受布尔值")
counts[value] += 1
return [value for value, count in counts.items() if count > 1]
assert duplicated_values([7, 3, 7, 3, 7]) == [7, 3]
assert duplicated_values([]) == []
assert duplicated_values([2, 3, 3, 2]) == [2, 3]
这里利用Counter保存首次插入顺序的行为。最后一个断言有意区分两种顺序:首次出现顺序是2、3,首次“被发现重复”的顺序却是3、2。
如果题目没有要求输出顺序,也应说清自己的选择,避免测试和实现各自假定一种口径。
第二层:输入里混进True,为什么结果可能变了
Python里,True与1的相等比较为真,布尔值也属于int的子类。若直接把它们放进依赖相等性与哈希的计数结构,就可能被视为同一个键。
所以示例没有用isinstance(value, int),而是明确排除了布尔值。这不是说所有产品都必须这样做,而是当前函数契约只接受严格的整数输入。
还可以继续问:负数算不算合法?是否允许空输入?数字字符串"7"与整数7是不是同一项?如果业务处理的是编号,前导零是否有意义?
这些问题不需要全部塞进一个算法题里,但你应该知道,选择一种数据结构之前,先要知道业务认为什么东西相等。
测试样本可以这样准备:
-
全部不同,检查没有误报。 -
一个值多次出现,检查只输出一次。 -
多个重复值交错出现,检查计数与顺序。 -
空输入、负数和零,按已确认契约验收。 -
布尔值、字符串等非约定类型,明确拒绝或明确转换。
第三层:数据放不进内存,还能不能这么做
对可哈希的普通整数,在通常的哈希表条件下,计数方案的期望时间随输入规模线性增长;空间主要取决于不同值的数量。
“线性时间”不代表不用考虑内存。几亿个不同值各保留一个计数,字典本身也需要空间。生成器可以避免一次性保存全部输入,却不能让计数表凭空消失。
如果数据已经排序,可以逐组统计相邻相同值;如果输入远大于内存,可以考虑外部排序后分组,或者按稳定规则分区,再在每个分区内计数。分区方案仍要处理倾斜和过大的分区,不能只说“哈希一下就行”。
若业务只需要判断“可能见过没有”,某些近似结构可能有用;但如果要求精确列出重复值,就不能把有误判概率的结果直接当成最终答案。
AI能秒写函数,你要能说出它漏了哪个条件
让AI写找重复数字的代码并不难。更能体现测试开发思路的练习,是拿同一份输入定义去核验它:输出值还是次数,顺序如何处理,非法类型怎么办,数据规模变化后还有什么限制。
可以把问题分成三步回答:
“我先确认重复的定义和输出顺序。对可放进内存的输入,用计数表统计,再按约定返回结果。如果规模很大,会评估不同值数量和可用内存,再选择排序或分区方案,并保留对应的异常与边界测试。”
这段回答的价值,在于每一步都能继续展开一个例子。面试官换一组数据,你仍然知道自己在验证什么。
下次遇到看起来只有一行set的题,先写出一个你认为正确的输入输出对。把问题说清楚,代码才有办法写对。
- 点赞
- 收藏
- 关注作者
评论(0)