代表性面试主题

编程面试:如何原地求下一个字典序排列?

编程题中等
Offer.cc 编辑团队发布 更新

题干

给定可能含重复值的整数数组,请原地改成字典序严格更大的下一个排列;若不存在则改成最小排列。请说明拐点、交换、后缀处理与边界。

面试官考察点

给定可能含重复值的整数数组,请原地改成字典序严格更大的下一个排列;若当前已经是最大排列,则改成升序的最小排列。

约束与边界

  • 只能使用 O(1) 额外空间,允许交换和反转。
  • 重复值不能被当成不同元素计数,但比较仍按数值进行。
  • 数组为空或只有一个元素时,操作保持不变。
  • 必须是全局字典序的下一个排列,不能只交换相邻元素。

找到最右侧拐点

从右向左找到第一个满足“左值严格小于右值”的位置 i。其右侧已经是非递增后缀;如果找不到拐点,整个数组降序,反转即可得到最小排列。

交换后再最小化后缀

有拐点时,从右向左找第一个大于 nums[i] 的元素 j。因为后缀非递增,首次找到的就是最小的可行更大值。交换 ij,再反转 i + 1 之后的后缀,使变化幅度最小。

30 秒回答框架

“我从右找第一个上升拐点 i。若不存在,数组已是最大排列,直接反转。否则从右找第一个大于 nums[i]j,交换两者,再把右侧后缀反转成升序。后缀原本非递增,所以这三步得到严格更大的最小候选,时间 O(n)、空间 O(1)。”

回答前需要澄清的问题

  • 是否要求原地修改?若允许额外空间,可以排序复制;原地要求决定反转方案。
  • “下一个”按数字升序还是字符串比较?负数和多位数时语义不同。
  • 元素是否可能重复?重复值决定寻找严格大于而不是大于等于。

分步骤深入解答

设数组为 [1,2,3],拐点是 1 的位置,交换后缀中最小的大于它的 2,得到 [2,1,3],再反转后缀得到 [2,1,3]。对 [3,2,1] 找不到拐点,反转成 [1,2,3]

text
i = n - 2
while i >= 0 and nums[i] >= nums[i + 1]:
    i -= 1
if i >= 0:
    j = n - 1
    while nums[j] <= nums[i]:
        j -= 1
    swap(nums[i], nums[j])
reverse(nums, i + 1, n - 1)

等值元素使用“大于等于”关系跳过拐点候选,使用“小于等于”关系跳过交换候选,保证变化严格增加。后缀反转而非排序,是因为拐点右侧已经有序,反转可在线性时间完成。

高质量示范回答

“字典序下一个排列要先让尽可能靠右的位置变大,同时让后缀尽可能小。我从右找最右的‘左值严格小于右值’拐点;从右找第一个大于它的值交换,随后反转后缀。没有拐点说明已是最大排列,反转整个数组。扫描、交换和反转都为 O(n),只用常数变量。”

常见错误

  • 从左找拐点,导致改变了更高位,跳过中间排列。
  • 交换后直接结束,没有把后缀变成最小顺序。
  • 用大于等于寻找交换值,重复值可能无法严格变大。
  • 对后缀调用通用排序,空间或复杂度超出原地要求。
  • 没有处理完全降序数组,返回原数组而不是最小排列。

错误表现与修正

[1,3,2] 若返回 [3,1,2],说明拐点选得太靠左;正确结果是 [2,1,3]。对 [1,1,5] 若交换相等元素,说明严格比较边界错误。

生产化实现

封装一个只接收可变随机访问序列的函数,并让反转使用双指针。若元素类型可能溢出比较或跨语言比较规则不同,在接口层明确排序关系和异常输入策略。

验证清单

测试空数组、单元素、完全升序、完全降序、重复值、拐点在末端和多个最优排列。对小数组生成所有不重复排列,按字典序排序后逐个验证函数输出的下一个元素。

追问及应对

为什么一定要找最右拐点?

越靠右的拐点代表越小的高位变化;在它右侧寻找交换值并最小化后缀,才能得到当前排列之后的紧邻排列。

后缀为什么可以直接反转?

拐点是从右找到的,因此右侧原本非递增。交换一个更大值后,剩余后缀仍可通过反转恢复为升序最小状态。

如果要求第 k 个下一个排列呢?

重复执行会产生 O(k n) 时间;若 k 很大,可研究计数排列、跳跃或直接按排名解码,但需要额外的组合计数与重复值处理。

评分标准

  • 拐点正确:从右寻找最右的严格上升位置。
  • 交换正确:从右寻找第一个严格更大的值。
  • 后缀正确:交换后反转为最小升序。
  • 边界完整:覆盖降序、重复、空数组和单元素。
  • 复杂度准确:O(n) 时间、O(1) 额外空间。

合规检查

确认三步算法、边界样例和复杂度结论在回答中保持一致。

面试作答要点

先解释“右侧最小变化”,再写拐点、交换和反转三步;用一个重复值例子说明严格比较,最后给出复杂度和全排列对拍。

一句话总结

下一个排列通过最右拐点、最小可行交换值和后缀反转,在原地 O(n) 时间得到字典序紧邻结果。

公开来源

同类题目

相关面试工具

用 Screenshot 处理算法题

截图题目后,按顺序看约束、解法、代码、边界条件和复杂度。

查看工具