Frontend SWE / IS&T · Phone Coding

Apple 前端数组题:重复值置零再手写排序

约 75 个乱序整数中稳定识别重复值,手写归并排序并说明零、空间和禁用库排序的边界。

题目重点:约 75 个乱序整数中稳定识别重复值,手写归并排序并说明零、空间和禁用库排序的边界。

题意与假设

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

  • 训练版保留每个数第一次出现的值,把其后重复出现位置替为整数 0,然后对整个等长列表升序排序;原始 0 也是合法输入。
  • 代码返回新列表、不修改输入;允许 O(n) 辅助空间,且禁止调用内置 sort 或手写 quicksort/bubblesort。若面试官要求原地或零放末尾,须调整输出规则。

解题路线

  1. 一遍维护 seen 集合:首见值原样放入结果,其后相同值写 0。原始 0 首次也是 0,之后重复 0 仍为 0,最终值列表无法区分两者,但排序目标仍确定。
  2. 手写 merge sort:递归二分,合并两个已排序子列;不会用题面提到的原生排序、quicksort 或 bubblesort。
  3. 如果额外空间被禁止,可考虑先原地排序后处理相邻重复,再重排零,但会改变‘保留第一次位置’这类潜在规则;需先问清楚。
  4. 训练实现对 97,656 个短数组用独立 O(n²) 首次出现判定加内置排序作为测试 oracle。

手动走一遍

[3,1,3,0,1] 首次 3/1/0 保留,第二个 3 与第二个 1 变 0,得到 [3,1,0,0,0];升序结果 [0,0,0,1,3]。若题目希望所有原始零保留且新零有来源标签,单个整数 0 已不足以表达该信息。

Python 参考实现

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

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

def zero_duplicates_and_sort(values: list[int]) -> list[int]:
    """Keep first occurrence, replace later duplicates with zero, then sort."""
    seen = set()
    transformed = []
    for value in values:
        if value in seen:
            transformed.append(0)
        else:
            seen.add(value)
            transformed.append(value)

    def merge_sort(items: list[int]) -> list[int]:
        if len(items) <= 1:
            return items[:]
        middle = len(items) // 2
        left = merge_sort(items[:middle])
        right = merge_sort(items[middle:])
        merged = []
        i = j = 0
        while i < len(left) and j < len(right):
            if left[i] <= right[j]:
                merged.append(left[i])
                i += 1
            else:
                merged.append(right[j])
                j += 1
        merged.extend(left[i:])
        merged.extend(right[j:])
        return merged

    return merge_sort(transformed)

复杂度

判重平均 O(n) 时间、O(u) seen 空间;归并排序 O(n log n) 时间、O(n) 辅助空间,递归栈 O(log n)。输入约 75 只说明该样本规模,未给小值域,不能假设 counting sort 适用。

易错点与追问

容易失分的边界

  • 把所有出现过重复的数连第一次也置零,和‘后续重复’契约不一致。
  • 用系统 sort/内置 quicksort,违反公开限制。
  • 不问原地和零输出位置,却把一种格式冒充原题唯一要求。
  • 输入原本含 0 时把零视为占位符从判重集合排除。
  • 声称 counting sort O(n) 却没有整数值域上界。

继续深挖

  • 若要求原地 O(1) 额外空间,能否用堆排序并怎样标记重复?
  • 如果只返回去重后的非零值加一个重复数量,如何减少空间?
  • 若输入非常大而范围很小,计数排序何时合适?
  • 如何保证原始 0 与重复替换出的 0 能被区分?
✓已复制微信号!