Data Engineer · Tech Screen Python

Microsoft DE 频次排序:频次升序、同频数值降序与手写归并

不用内置排序,按 (出现次数升序, 数值降序) 排列数组;用频次表加自写归并排序实现并分析稳定性。

题目重点:不用内置排序,按 (出现次数升序, 数值降序) 排列数组;用频次表加自写归并排序实现并分析稳定性。

题意与假设

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

  • 输入是整数列表;输出包含原数组每个元素及其重复次数,只改顺序。
  • 不能调用 built-in sort/sorted;允许使用哈希计数和手写归并排序。
  • 同一数值元素完全等价,无需区分它们原来的身份。

解题路线

  1. 先一遍 Counter 计算每个整数的全局出现次数。比较器先比 freq[a] 与 freq[b],较小者在前;频次相同则数值较大的在前。
  2. 用递归归并排序:左右半区各排序,再按比较器归并。相等时优先取左侧,保持稳定性,虽然同值元素不可区分。
  3. 对每个输入位置排序,结果自然保留重复项;如果先排序 distinct keys,也可以再按频次展开,但必须明确展开发生在排序之后。
  4. 手写排序的重点是比较器满足全序;不应偷偷调用语言内置排序绕过面试官约束。

手动走一遍

[2,3,1,3,2] 中 1 出现 1 次、2 和 3 各出现 2 次。1 在前;同频 3 大于 2,故输出 [1,3,3,2,2]。负数比较仍按整数大小,-1 大于 -2。

Python 参考实现

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

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

from collections import Counter
from collections.abc import Sequence


def frequency_sort(values: Sequence[int]) -> list[int]:
    """Frequency ascending, value descending on ties; no built-in sort."""
    freq = Counter(values)
    items = list(values)

    def before(a: int, b: int) -> bool:
        return (freq[a], -a) <= (freq[b], -b)

    def merge_sort(part: list[int]) -> list[int]:
        if len(part) <= 1:
            return part
        mid = len(part) // 2
        left, right = merge_sort(part[:mid]), merge_sort(part[mid:])
        result = []
        i = j = 0
        while i < len(left) and j < len(right):
            if before(left[i], right[j]):
                result.append(left[i]); i += 1
            else:
                result.append(right[j]); j += 1
        result.extend(left[i:]); result.extend(right[j:])
        return result

    return merge_sort(items)


if __name__ == "__main__":
    assert frequency_sort([2, 3, 1, 3, 2]) == [1, 3, 3, 2, 2]

复杂度

计数 O(n) 预期时间、O(d) 空间,归并 O(n log n) 时间、O(n) 辅助空间,加递归栈 O(log n),d 为不同值数。代码切片会复制子数组,但总额外峰值仍为 O(n) 量级。

易错点与追问

容易失分的边界

  • 把同频数值按升序排。
  • 只输出不同整数,丢掉重复项。
  • 用 Counter.most_common 或 sorted 违反‘自己写 sort’约束。
  • 归并比较器前后分支不一致,导致排序非传递。
  • 将 SQL merge 的未知表结构补成具体题目;题目未明确。

继续深挖

  • 若数据量大但数值范围小,计数桶如何减少比较排序成本?
  • 数据放不进内存时,如何先聚合频次再外部排序?
  • 如果要稳定保留同键对象的原位置,需要在排序项中保存什么?
✓已复制微信号!