SDE / Frontend · Coding

Amazon Frontend:朋友买过而自己没买过,怎样按产品 ID 求推荐?

从原user/purchase/product结构求集合差,处理重复朋友、重复购买与同名产品。

题目重点:从原user/purchase/product结构求集合差,处理重复朋友、重复购买与同名产品。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 题目有user.friends/user.purchases、purchase.productId/amount/purchaseId、productId/productName,问当前用户朋友买过但自己没买过的产品名称。
  • 本文以users[id]与products[id]映射规范化原对象引用;只直接朋友、不递归朋友的朋友;重复购买不增加推荐权重,自己购买过一次即排除。
  • 数据一致性合同:朋友ID和推荐产品ID存在,缺失时示例抛错误;输出按product ID排序,每个产品一次,同名不同ID可出现重复名称。这些原题未给规则待核。

解题路线

  1. 先扫描当前用户购买记录建立own productId集合。购买条数与amount不是同一个计量:本题是是否购买过,不以amount求热度,amount=0是否代表有效交易要业务确认,训练输入全部视作购买记录。
  2. 把直接朋友去重,忽略异常自引用,再扫描朋友的购买。若productId不在own就加入候选集合,最后通过产品字典转名称;不要先按名称求差,两个不同产品可能同名。
  3. 原friends是user[]时可直接遍历对象,但对象identity与业务userID需区分。训练代码用ID映射防重复与更易跨数据源join;如果原对象无ID,先确认同一人的身份规则,不虚构业务去重键。
  4. 排序是本文确定性输出合同,不表示原面试要求。若原题只比较无序集合,可省排序;若要按朋友数量排名,则需每个friend/product对先去重再计票,而不是按购买次数累计。
  5. 大数据可数据库anti-join:朋友购买与产品join后排除当前用户购买、distinct productID;避免每朋友单次查询造成N+1。快照/缓存版本须一致,撤单、退货、好友可见范围都是额外业务合同。
  6. 独立验证逐个产品检查“任一直接朋友买过且自己未买”,而非复制主算法的集合差步骤;20,000用户查询包含重复朋友、同名产品和重复purchase。

手动走一遍

当前用户买p1;朋友甲买p1,p2,p2,朋友乙买p3。返回p2,p3的名称。若p2与p3都叫“Mouse”,本文输出[Mouse,Mouse]对应两个ID;若要求唯一名称应再去重,但这会丢失产品身份,必须向面试官确认。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

def friends_unbought(user_id, users, products):
    """Training maps normalize source object references to unique user/product IDs."""
    user = users[user_id]
    bought = {p['productId'] for p in user['purchases']}
    recommended = set()
    for friend_id in set(user['friends']):
        if friend_id == user_id:
            continue
        for purchase in users[friend_id]['purchases']:
            pid = purchase['productId']
            if pid not in bought:
                recommended.add(pid)
    # Distinct products can have the same name. Return one entry per product ID.
    return [products[pid] for pid in sorted(recommended)]

复杂度

own记录A,去重朋友F,其购买总P,候选R:期望O(A+F+P+R log R),哈希状态O(A+F+R),产品索引若需新建另加全catalog成本。原对象图全量不必递归扫描。

易错点与追问

容易失分的边界

  • 按name去重产品。
  • amount当是否买过的默认门槛。
  • 查到朋友的朋友改变范围。
  • 重复friend把票多算。
  • 无catalog记录默默返回假名称。

继续深挖

  • 排名要按朋友人数怎样去重?
  • 撤单/退款怎样定义买过?
  • catalog缺失怎样输出待核项?
  • 百万朋友记录怎样分页/快照?
✓已复制微信号!