SWE · Coding

Meta Next/Previous Permutation:枢轴、后缀与重复值

原地求字典序相邻排列,无下一项时按明确政策回绕,并合并前一排列变体。

题目重点:原地求字典序相邻排列,无下一项时按明确政策回绕,并合并前一排列变体。

题意与假设

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

  • 训练values可比较、重复元素合法;无strict next时回到最小排列,无strict previous回到最大,函数返回是否存在严格邻居。空/单元素无严格邻居。
  • Previous方向来自明确追问,数值比较与字典序合同固定,不枚举全部排列。

解题路线

  1. next从右找到最后values[i]<values[i+1]枢轴;右侧为非递增后缀,已经是该前缀下最大状态。没有枢轴则整个序列最大,reverse回最小。
  2. 在后缀从右找到最小但仍大于pivot的值交换,因为后缀非递增、最右符合者即最小更大。之后reverse后缀,使新前缀下余部分最小,得到最小严格更大排列。
  3. previous对称,找最后下降枢轴、换后缀中最大较小者,再reverse后缀得到最大的较小排列。统一before比较函数切方向,不能只swap两个固定元素。
  4. 重复值比较必须严格,等值交换不能产生新排列;后缀reverse仍正确,因为单调方向非严格。
  5. 原地双指针reverse不用切片副本。返回False只是strict邻居不存在,但训练仍会修改回绕;如果面试要求不改或None,应调整合同。

手动走一遍

[1,2,3]next为[1,3,2];[1,1,5]next为[1,5,1]。最大[3,2,1]next回[1,2,3]且False;[1,3,2]previous为[1,2,3]。

Python 参考实现

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

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

def advance_permutation(values,previous=False):
    """Modify list, duplicates supported; wraps to first/last when no strict next.

    Return True if strict neighbor existed, False if wrapped.
    """
    def before(a,b):return a>b if previous else a<b
    pivot=len(values)-2
    while pivot>=0 and not before(values[pivot],values[pivot+1]):pivot-=1
    found=pivot>=0
    if found:
        successor=len(values)-1
        while not before(values[pivot],values[successor]):successor-=1
        values[pivot],values[successor]=values[successor],values[pivot]
    left=pivot+1;right=len(values)-1
    while left<right:
        values[left],values[right]=values[right],values[left];left+=1;right-=1
    return found

复杂度

O(N)时间/O(1)辅助空间,不生成阶乘个候选。独立穷举小multiset所有distinct排列按字典序对照,可以验证重复值和wrap。

易错点与追问

容易失分的边界

  • 找第一个左侧上升而非最右,会跳过许多排列。
  • 交换后不最小化后缀,结果更大但不是紧邻。

继续深挖

  • 第K个排列与相邻排列是不同任务,重复值会怎样改变计数?
  • 自定义比较器需要满足全序,否则单调后缀证明不成立。
✓已复制微信号!